// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/js-call-reducer.h"

#include "src/api-inl.h"
#include "src/builtins/builtins-promise.h"
#include "src/builtins/builtins-utils.h"
#include "src/code-factory.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/access-info.h"
#include "src/compiler/allocation-builder.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/property-access-builder.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/type-cache.h"
#include "src/counters.h"
#include "src/feedback-vector-inl.h"
#include "src/ic/call-optimization.h"
#include "src/objects-inl.h"
#include "src/objects/arguments-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-objects.h"
#include "src/objects/ordered-hash-table.h"
#include "src/vector-slot-pair.h"

#include "src/objects/ordered-hash-table-inl.h" // weolar

namespace v8 {
namespace internal {
    namespace compiler {

        Reduction JSCallReducer::ReduceMathUnary(Node* node, const Operator* op)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->NaNConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* input = NodeProperties::GetValueInput(node, 2);

            input = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                  NumberOperationHint::kNumberOrOddball, p.feedback()),
                input, effect, control);
            Node* value = graph()->NewNode(op, input);
            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        Reduction JSCallReducer::ReduceMathBinary(Node* node, const Operator* op)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->NaNConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            Node* left = NodeProperties::GetValueInput(node, 2);
            Node* right = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->NaNConstant();
            left = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                 NumberOperationHint::kNumberOrOddball, p.feedback()),
                left, effect, control);
            right = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                  NumberOperationHint::kNumberOrOddball, p.feedback()),
                right, effect, control);
            Node* value = graph()->NewNode(op, left, right);
            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        // ES6 section 20.2.2.19 Math.imul ( x, y )
        Reduction JSCallReducer::ReduceMathImul(Node* node)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->ZeroConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            Node* left = NodeProperties::GetValueInput(node, 2);
            Node* right = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->ZeroConstant();
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            left = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                 NumberOperationHint::kNumberOrOddball, p.feedback()),
                left, effect, control);
            right = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                  NumberOperationHint::kNumberOrOddball, p.feedback()),
                right, effect, control);
            left = graph()->NewNode(simplified()->NumberToUint32(), left);
            right = graph()->NewNode(simplified()->NumberToUint32(), right);
            Node* value = graph()->NewNode(simplified()->NumberImul(), left, right);
            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        // ES6 section 20.2.2.11 Math.clz32 ( x )
        Reduction JSCallReducer::ReduceMathClz32(Node* node)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->Constant(32);
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            Node* input = NodeProperties::GetValueInput(node, 2);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            input = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                  NumberOperationHint::kNumberOrOddball, p.feedback()),
                input, effect, control);
            input = graph()->NewNode(simplified()->NumberToUint32(), input);
            Node* value = graph()->NewNode(simplified()->NumberClz32(), input);
            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        // ES6 section 20.2.2.24 Math.max ( value1, value2, ...values )
        // ES6 section 20.2.2.25 Math.min ( value1, value2, ...values )
        Reduction JSCallReducer::ReduceMathMinMax(Node* node, const Operator* op,
            Node* empty_value)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() <= 2) {
                ReplaceWithValue(node, empty_value);
                return Replace(empty_value);
            }
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            Node* value = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                        NumberOperationHint::kNumberOrOddball, p.feedback()),
                NodeProperties::GetValueInput(node, 2), effect, control);
            for (int i = 3; i < node->op()->ValueInputCount(); i++) {
                Node* input = effect = graph()->NewNode(
                    simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
                        p.feedback()),
                    NodeProperties::GetValueInput(node, i), effect, control);
                value = graph()->NewNode(op, value, input);
            }

            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        Reduction JSCallReducer::Reduce(Node* node)
        {
            switch (node->opcode()) {
            case IrOpcode::kJSConstruct:
                return ReduceJSConstruct(node);
            case IrOpcode::kJSConstructWithArrayLike:
                return ReduceJSConstructWithArrayLike(node);
            case IrOpcode::kJSConstructWithSpread:
                return ReduceJSConstructWithSpread(node);
            case IrOpcode::kJSCall:
                return ReduceJSCall(node);
            case IrOpcode::kJSCallWithArrayLike:
                return ReduceJSCallWithArrayLike(node);
            case IrOpcode::kJSCallWithSpread:
                return ReduceJSCallWithSpread(node);
            default:
                break;
            }
            return NoChange();
        }

        void JSCallReducer::Finalize()
        {
            // TODO(turbofan): This is not the best solution; ideally we would be able
            // to teach the GraphReducer about arbitrary dependencies between different
            // nodes, even if they don't show up in the use list of the other node.
            std::set<Node*> const waitlist = std::move(waitlist_);
            for (Node* node : waitlist) {
                if (!node->IsDead()) {
                    Reduction const reduction = Reduce(node);
                    if (reduction.Changed()) {
                        Node* replacement = reduction.replacement();
                        if (replacement != node) {
                            Replace(node, replacement);
                        }
                    }
                }
            }
        }

        // ES6 section 22.1.1 The Array Constructor
        Reduction JSCallReducer::ReduceArrayConstructor(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* target = NodeProperties::GetValueInput(node, 0);
            CallParameters const& p = CallParametersOf(node->op());

            // Turn the {node} into a {JSCreateArray} call.
            DCHECK_LE(2u, p.arity());
            size_t const arity = p.arity() - 2;
            NodeProperties::ReplaceValueInput(node, target, 0);
            NodeProperties::ReplaceValueInput(node, target, 1);
            NodeProperties::ChangeOp(
                node, javascript()->CreateArray(arity, MaybeHandle<AllocationSite>()));
            return Changed(node);
        }

        // ES6 section 19.3.1.1 Boolean ( value )
        Reduction JSCallReducer::ReduceBooleanConstructor(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());

            // Replace the {node} with a proper {ToBoolean} operator.
            DCHECK_LE(2u, p.arity());
            Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant()
                                           : NodeProperties::GetValueInput(node, 2);
            value = graph()->NewNode(simplified()->ToBoolean(), value);
            ReplaceWithValue(node, value);
            return Replace(value);
        }

        // ES section #sec-object-constructor
        Reduction JSCallReducer::ReduceObjectConstructor(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.arity() < 3)
                return NoChange();
            Node* value = (p.arity() >= 3) ? NodeProperties::GetValueInput(node, 2)
                                           : jsgraph()->UndefinedConstant();
            Node* effect = NodeProperties::GetEffectInput(node);

            // We can fold away the Object(x) call if |x| is definitely not a primitive.
            if (NodeProperties::CanBePrimitive(broker(), value, effect)) {
                if (!NodeProperties::CanBeNullOrUndefined(broker(), value, effect)) {
                    // Turn the {node} into a {JSToObject} call if we know that
                    // the {value} cannot be null or undefined.
                    NodeProperties::ReplaceValueInputs(node, value);
                    NodeProperties::ChangeOp(node, javascript()->ToObject());
                    return Changed(node);
                }
            } else {
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            return NoChange();
        }

        // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
        Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            size_t arity = p.arity();
            DCHECK_LE(2u, arity);
            ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
            if (arity == 2) {
                // Neither thisArg nor argArray was provided.
                convert_mode = ConvertReceiverMode::kNullOrUndefined;
                node->ReplaceInput(0, node->InputAt(1));
                node->ReplaceInput(1, jsgraph()->UndefinedConstant());
            } else if (arity == 3) {
                // The argArray was not provided, just remove the {target}.
                node->RemoveInput(0);
                --arity;
            } else {
                Node* target = NodeProperties::GetValueInput(node, 1);
                Node* this_argument = NodeProperties::GetValueInput(node, 2);
                Node* arguments_list = NodeProperties::GetValueInput(node, 3);
                Node* context = NodeProperties::GetContextInput(node);
                Node* frame_state = NodeProperties::GetFrameStateInput(node);
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);

                // If {arguments_list} cannot be null or undefined, we don't need
                // to expand this {node} to control-flow.
                if (!NodeProperties::CanBeNullOrUndefined(broker(), arguments_list,
                        effect)) {
                    // Massage the value inputs appropriately.
                    node->ReplaceInput(0, target);
                    node->ReplaceInput(1, this_argument);
                    node->ReplaceInput(2, arguments_list);
                    while (arity-- > 3)
                        node->RemoveInput(3);

                    // Morph the {node} to a {JSCallWithArrayLike}.
                    NodeProperties::ChangeOp(node,
                        javascript()->CallWithArrayLike(p.frequency()));
                    Reduction const reduction = ReduceJSCallWithArrayLike(node);
                    return reduction.Changed() ? reduction : Changed(node);
                } else {
                    // Check whether {arguments_list} is null.
                    Node* check_null = graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
                        jsgraph()->NullConstant());
                    control = graph()->NewNode(common()->Branch(BranchHint::kFalse),
                        check_null, control);
                    Node* if_null = graph()->NewNode(common()->IfTrue(), control);
                    control = graph()->NewNode(common()->IfFalse(), control);

                    // Check whether {arguments_list} is undefined.
                    Node* check_undefined = graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
                        jsgraph()->UndefinedConstant());
                    control = graph()->NewNode(common()->Branch(BranchHint::kFalse),
                        check_undefined, control);
                    Node* if_undefined = graph()->NewNode(common()->IfTrue(), control);
                    control = graph()->NewNode(common()->IfFalse(), control);

                    // Lower to {JSCallWithArrayLike} if {arguments_list} is neither null
                    // nor undefined.
                    Node* effect0 = effect;
                    Node* control0 = control;
                    Node* value0 = effect0 = control0 = graph()->NewNode(
                        javascript()->CallWithArrayLike(p.frequency()), target, this_argument,
                        arguments_list, context, frame_state, effect0, control0);

                    // Lower to {JSCall} if {arguments_list} is either null or undefined.
                    Node* effect1 = effect;
                    Node* control1 = graph()->NewNode(common()->Merge(2), if_null, if_undefined);
                    Node* value1 = effect1 = control1 = graph()->NewNode(javascript()->Call(2), target, this_argument,
                        context, frame_state, effect1, control1);

                    // Rewire potential exception edges.
                    Node* if_exception = nullptr;
                    if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
                        // Create appropriate {IfException} and {IfSuccess} nodes.
                        Node* if_exception0 = graph()->NewNode(common()->IfException(), control0, effect0);
                        control0 = graph()->NewNode(common()->IfSuccess(), control0);
                        Node* if_exception1 = graph()->NewNode(common()->IfException(), control1, effect1);
                        control1 = graph()->NewNode(common()->IfSuccess(), control1);

                        // Join the exception edges.
                        Node* merge = graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
                        Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
                            if_exception1, merge);
                        Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                            if_exception0, if_exception1, merge);
                        ReplaceWithValue(if_exception, phi, ephi, merge);
                    }

                    // Join control paths.
                    control = graph()->NewNode(common()->Merge(2), control0, control1);
                    effect = graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control);
                    Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                        value0, value1, control);
                    ReplaceWithValue(node, value, effect, control);
                    return Replace(value);
                }
            }
            // Change {node} to the new {JSCall} operator.
            NodeProperties::ChangeOp(
                node,
                javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
            // Try to further reduce the JSCall {node}.
            Reduction const reduction = ReduceJSCall(node);
            return reduction.Changed() ? reduction : Changed(node);
        }

        // ES section #sec-function.prototype.bind
        Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            // Value inputs to the {node} are as follows:
            //
            //  - target, which is Function.prototype.bind JSFunction
            //  - receiver, which is the [[BoundTargetFunction]]
            //  - bound_this (optional), which is the [[BoundThis]]
            //  - and all the remaining value inouts are [[BoundArguments]]
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* bound_this = (node->op()->ValueInputCount() < 3)
                ? jsgraph()->UndefinedConstant()
                : NodeProperties::GetValueInput(node, 2);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Ensure that the {receiver} is known to be a JSBoundFunction or
            // a JSFunction with the same [[Prototype]], and all maps we've
            // seen for the {receiver} so far indicate that {receiver} is
            // definitely a constructor or not a constructor.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());
            MapRef first_receiver_map(broker(), receiver_maps[0]);
            bool const is_constructor = first_receiver_map.is_constructor();
            first_receiver_map.SerializePrototype();
            ObjectRef const prototype = first_receiver_map.prototype();
            for (Handle<Map> const map : receiver_maps) {
                MapRef receiver_map(broker(), map);

                // Check for consistency among the {receiver_maps}.
                STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE);
                receiver_map.SerializePrototype();
                if (!receiver_map.prototype().equals(prototype) || receiver_map.is_constructor() != is_constructor || receiver_map.instance_type() < FIRST_FUNCTION_TYPE) {
                    return NoChange();
                }

                // Disallow binding of slow-mode functions. We need to figure out
                // whether the length and name property are in the original state.
                if (receiver_map.is_dictionary_map())
                    return NoChange();

                // Check whether the length and name properties are still present
                // as AccessorInfo objects. In that case, their values can be
                // recomputed even if the actual value of the object changes.
                // This mirrors the checks done in builtins-function-gen.cc at
                // runtime otherwise.
                Handle<DescriptorArray> descriptors(
                    receiver_map.object()->instance_descriptors(), isolate());
                if (descriptors->number_of_descriptors() < 2)
                    return NoChange();
                if (descriptors->GetKey(JSFunction::kLengthDescriptorIndex) != ReadOnlyRoots(isolate()).length_string()) {
                    return NoChange();
                }
                if (!descriptors->GetStrongValue(JSFunction::kLengthDescriptorIndex)
                         ->IsAccessorInfo()) {
                    return NoChange();
                }
                if (descriptors->GetKey(JSFunction::kNameDescriptorIndex) != ReadOnlyRoots(isolate()).name_string()) {
                    return NoChange();
                }
                if (!descriptors->GetStrongValue(JSFunction::kNameDescriptorIndex)
                         ->IsAccessorInfo()) {
                    return NoChange();
                }
            }

            // Choose the map for the resulting JSBoundFunction (but bail out in case of a
            // custom prototype).
            MapRef map = is_constructor
                ? native_context().bound_function_with_constructor_map()
                : native_context().bound_function_without_constructor_map();
            if (!map.prototype().equals(prototype))
                return NoChange();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, VectorSlotPair(), receiver, effect, control);

            // Replace the {node} with a JSCreateBoundFunction.
            int const arity = std::max(0, node->op()->ValueInputCount() - 3);
            int const input_count = 2 + arity + 3;
            Node** inputs = graph()->zone()->NewArray<Node*>(input_count);
            inputs[0] = receiver;
            inputs[1] = bound_this;
            for (int i = 0; i < arity; ++i) {
                inputs[2 + i] = NodeProperties::GetValueInput(node, 3 + i);
            }
            inputs[2 + arity + 0] = context;
            inputs[2 + arity + 1] = effect;
            inputs[2 + arity + 2] = control;
            Node* value = effect = graph()->NewNode(javascript()->CreateBoundFunction(arity, map.object()),
                input_count, inputs);
            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
        Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            Node* target = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Change context of {node} to the Function.prototype.call context,
            // to ensure any exception is thrown in the correct context.
            Node* context;
            HeapObjectMatcher m(target);
            if (m.HasValue()) {
                JSFunctionRef function = m.Ref(broker()).AsJSFunction();
                context = jsgraph()->Constant(function.context());
            } else {
                context = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
                    effect, control);
            }
            NodeProperties::ReplaceContextInput(node, context);
            NodeProperties::ReplaceEffectInput(node, effect);

            // Remove the target from {node} and use the receiver as target instead, and
            // the thisArg becomes the new target.  If thisArg was not provided, insert
            // undefined instead.
            size_t arity = p.arity();
            DCHECK_LE(2u, arity);
            ConvertReceiverMode convert_mode;
            if (arity == 2) {
                // The thisArg was not provided, use undefined as receiver.
                convert_mode = ConvertReceiverMode::kNullOrUndefined;
                node->ReplaceInput(0, node->InputAt(1));
                node->ReplaceInput(1, jsgraph()->UndefinedConstant());
            } else {
                // Just remove the target, which is the first value input.
                convert_mode = ConvertReceiverMode::kAny;
                node->RemoveInput(0);
                --arity;
            }
            NodeProperties::ChangeOp(
                node,
                javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
            // Try to further reduce the JSCall {node}.
            Reduction const reduction = ReduceJSCall(node);
            return reduction.Changed() ? reduction : Changed(node);
        }

        // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V)
        Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* object = (node->op()->ValueInputCount() >= 3)
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
            // stack trace doesn't contain the @@hasInstance call; we have the
            // corresponding bug in the baseline case. Some massaging of the frame
            // state would be necessary here.

            // Morph this {node} into a JSOrdinaryHasInstance node.
            node->ReplaceInput(0, receiver);
            node->ReplaceInput(1, object);
            node->ReplaceInput(2, context);
            node->ReplaceInput(3, frame_state);
            node->ReplaceInput(4, effect);
            node->ReplaceInput(5, control);
            node->TrimInputCount(6);
            NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
            return Changed(node);
        }

        Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object)
        {
            Node* effect = NodeProperties::GetEffectInput(node);

            // Try to determine the {object} map.
            ZoneHandleSet<Map> object_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), object, effect, &object_maps);
            if (result != NodeProperties::kNoReceiverMaps) {
                MapRef candidate_map(broker(), object_maps[0]);
                candidate_map.SerializePrototype();
                ObjectRef candidate_prototype = candidate_map.prototype();

                // Check if we can constant-fold the {candidate_prototype}.
                for (size_t i = 0; i < object_maps.size(); ++i) {
                    MapRef object_map(broker(), object_maps[i]);
                    object_map.SerializePrototype();
                    if (IsSpecialReceiverInstanceType(object_map.instance_type()) || object_map.has_hidden_prototype() || !object_map.prototype().equals(candidate_prototype)) {
                        // We exclude special receivers, like JSProxy or API objects that
                        // might require access checks here; we also don't want to deal
                        // with hidden prototypes at this point.
                        return NoChange();
                    }
                    // The above check also excludes maps for primitive values, which is
                    // important because we are not applying [[ToObject]] here as expected.
                    DCHECK(!object_map.IsPrimitiveMap() && object_map.IsJSReceiverMap());
                    if (result == NodeProperties::kUnreliableReceiverMaps && !object_map.is_stable()) {
                        return NoChange();
                    }
                }
                if (result == NodeProperties::kUnreliableReceiverMaps) {
                    for (size_t i = 0; i < object_maps.size(); ++i) {
                        dependencies()->DependOnStableMap(MapRef(broker(), object_maps[i]));
                    }
                }
                Node* value = jsgraph()->Constant(candidate_prototype);
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            return NoChange();
        }

        // ES6 section 19.1.2.11 Object.getPrototypeOf ( O )
        Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* object = (node->op()->ValueInputCount() >= 3)
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            return ReduceObjectGetPrototype(node, object);
        }

        // ES section #sec-object.is
        Reduction JSCallReducer::ReduceObjectIs(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& params = CallParametersOf(node->op());
            int const argc = static_cast<int>(params.arity() - 2);
            Node* lhs = (argc >= 1) ? NodeProperties::GetValueInput(node, 2)
                                    : jsgraph()->UndefinedConstant();
            Node* rhs = (argc >= 2) ? NodeProperties::GetValueInput(node, 3)
                                    : jsgraph()->UndefinedConstant();
            Node* value = graph()->NewNode(simplified()->SameValue(), lhs, rhs);
            ReplaceWithValue(node, value);
            return Replace(value);
        }

        // ES6 section B.2.2.1.1 get Object.prototype.__proto__
        Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            return ReduceObjectGetPrototype(node, receiver);
        }

        // ES #sec-object.prototype.hasownproperty
        Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& params = CallParametersOf(node->op());
            int const argc = static_cast<int>(params.arity() - 2);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* name = (argc >= 1) ? NodeProperties::GetValueInput(node, 2)
                                     : jsgraph()->UndefinedConstant();
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // We can optimize a call to Object.prototype.hasOwnProperty if it's being
            // used inside a fast-mode for..in, so for code like this:
            //
            //   for (name in receiver) {
            //     if (receiver.hasOwnProperty(name)) {
            //        ...
            //     }
            //   }
            //
            // If the for..in is in fast-mode, we know that the {receiver} has {name}
            // as own property, otherwise the enumeration wouldn't include it. The graph
            // constructed by the BytecodeGraphBuilder in this case looks like this:

            // receiver
            //  ^    ^
            //  |    |
            //  |    +-+
            //  |      |
            //  |   JSToObject
            //  |      ^
            //  |      |
            //  |   JSForInNext
            //  |      ^
            //  +----+ |
            //       | |
            //  JSCall[hasOwnProperty]

            // We can constant-fold the {node} to True in this case, and insert
            // a (potentially redundant) map check to guard the fact that the
            // {receiver} map didn't change since the dominating JSForInNext. This
            // map check is only necessary when TurboFan cannot prove that there
            // is no observable side effect between the {JSForInNext} and the
            // {JSCall} to Object.prototype.hasOwnProperty.
            //
            // Also note that it's safe to look through the {JSToObject}, since the
            // Object.prototype.hasOwnProperty does an implicit ToObject anyway, and
            // these operations are not observable.
            if (name->opcode() == IrOpcode::kJSForInNext) {
                ForInMode const mode = ForInModeOf(name->op());
                if (mode != ForInMode::kGeneric) {
                    Node* object = NodeProperties::GetValueInput(name, 0);
                    Node* cache_type = NodeProperties::GetValueInput(name, 2);
                    if (object->opcode() == IrOpcode::kJSToObject) {
                        object = NodeProperties::GetValueInput(object, 0);
                    }
                    if (object == receiver) {
                        // No need to repeat the map check if we can prove that there's no
                        // observable side effect between {effect} and {name].
                        if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) {
                            Node* receiver_map = effect = graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
                                receiver, effect, control);
                            Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
                                receiver_map, cache_type);
                            effect = graph()->NewNode(
                                simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect,
                                control);
                        }
                        Node* value = jsgraph()->TrueConstant();
                        ReplaceWithValue(node, value, effect, control);
                        return Replace(value);
                    }
                }
            }

            return NoChange();
        }

        // ES #sec-object.prototype.isprototypeof
        Reduction JSCallReducer::ReduceObjectPrototypeIsPrototypeOf(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* value = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* effect = NodeProperties::GetEffectInput(node);

            // Ensure that the {receiver} is known to be a JSReceiver (so that
            // the ToObject step of Object.prototype.isPrototypeOf is a no-op).
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            for (Handle<Map> map : receiver_maps) {
                MapRef receiver_map(broker(), map);
                if (!receiver_map.IsJSReceiverMap())
                    return NoChange();
            }

            // We don't check whether {value} is a proper JSReceiver here explicitly,
            // and don't explicitly rule out Primitive {value}s, since all of them
            // have null as their prototype, so the prototype chain walk inside the
            // JSHasInPrototypeChain operator immediately aborts and yields false.
            NodeProperties::ReplaceValueInput(node, value, 0);
            NodeProperties::ReplaceValueInput(node, receiver, 1);
            for (int i = node->op()->ValueInputCount(); i-- > 2;) {
                node->RemoveInput(i);
            }
            NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
            return Changed(node);
        }

        // ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList )
        Reduction JSCallReducer::ReduceReflectApply(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            DCHECK_LE(0, arity);
            // Massage value inputs appropriately.
            node->RemoveInput(0);
            node->RemoveInput(0);
            while (arity < 3) {
                node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
            }
            while (arity-- > 3) {
                node->RemoveInput(arity);
            }
            NodeProperties::ChangeOp(node,
                javascript()->CallWithArrayLike(p.frequency()));
            Reduction const reduction = ReduceJSCallWithArrayLike(node);
            return reduction.Changed() ? reduction : Changed(node);
        }

        // ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] )
        Reduction JSCallReducer::ReduceReflectConstruct(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            DCHECK_LE(0, arity);
            // Massage value inputs appropriately.
            node->RemoveInput(0);
            node->RemoveInput(0);
            while (arity < 2) {
                node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
            }
            if (arity < 3) {
                node->InsertInput(graph()->zone(), arity++, node->InputAt(0));
            }
            while (arity-- > 3) {
                node->RemoveInput(arity);
            }
            NodeProperties::ChangeOp(node,
                javascript()->ConstructWithArrayLike(p.frequency()));
            Reduction const reduction = ReduceJSConstructWithArrayLike(node);
            return reduction.Changed() ? reduction : Changed(node);
        }

        // ES6 section 26.1.7 Reflect.getPrototypeOf ( target )
        Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* target = (node->op()->ValueInputCount() >= 3)
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            return ReduceObjectGetPrototype(node, target);
        }

        // ES6 section #sec-object.create Object.create(proto, properties)
        Reduction JSCallReducer::ReduceObjectCreate(Node* node)
        {
            int arg_count = node->op()->ValueInputCount();
            Node* properties = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3)
                                              : jsgraph()->UndefinedConstant();
            if (properties != jsgraph()->UndefinedConstant())
                return NoChange();

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* prototype = arg_count >= 3 ? NodeProperties::GetValueInput(node, 2)
                                             : jsgraph()->UndefinedConstant();
            node->ReplaceInput(0, prototype);
            node->ReplaceInput(1, context);
            node->ReplaceInput(2, frame_state);
            node->ReplaceInput(3, effect);
            node->ReplaceInput(4, control);
            node->TrimInputCount(5);
            NodeProperties::ChangeOp(node, javascript()->CreateObject());
            return Changed(node);
        }

        // ES section #sec-reflect.get
        Reduction JSCallReducer::ReduceReflectGet(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            if (arity != 2)
                return NoChange();
            Node* target = NodeProperties::GetValueInput(node, 2);
            Node* key = NodeProperties::GetValueInput(node, 3);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Check whether {target} is a JSReceiver.
            Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
            Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

            // Throw an appropriate TypeError if the {target} is not a JSReceiver.
            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            Node* efalse = effect;
            {
                if_false = efalse = graph()->NewNode(
                    javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
                    jsgraph()->Constant(
                        static_cast<int>(MessageTemplate::kCalledOnNonObject)),
                    jsgraph()->HeapConstant(factory()->ReflectGet_string()), context,
                    frame_state, efalse, if_false);
            }

            // Otherwise just use the existing GetPropertyStub.
            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            Node* etrue = effect;
            Node* vtrue;
            {
                Callable callable = Builtins::CallableFor(isolate(), Builtins::kGetProperty);
                auto call_descriptor = Linkage::GetStubCallDescriptor(
                    graph()->zone(), callable.descriptor(),
                    callable.descriptor().GetStackParameterCount(),
                    CallDescriptor::kNeedsFrameState, Operator::kNoProperties);
                Node* stub_code = jsgraph()->HeapConstant(callable.code());
                vtrue = etrue = if_true = graph()->NewNode(common()->Call(call_descriptor), stub_code, target,
                    key, context, frame_state, etrue, if_true);
            }

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                // Create appropriate {IfException} and {IfSuccess} nodes.
                Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
                if_true = graph()->NewNode(common()->IfSuccess(), if_true);
                Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
                if_false = graph()->NewNode(common()->IfSuccess(), if_false);

                // Join the exception edges.
                Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
                Node* ephi = graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
                Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    extrue, exfalse, merge);
                ReplaceWithValue(on_exception, phi, ephi, merge);
            }

            // Connect the throwing path to end.
            if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
            NodeProperties::MergeControlToEnd(graph(), common(), if_false);

            // Continue on the regular path.
            ReplaceWithValue(node, vtrue, etrue, if_true);
            return Changed(vtrue);
        }

        // ES section #sec-reflect.has
        Reduction JSCallReducer::ReduceReflectHas(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            DCHECK_LE(0, arity);
            Node* target = (arity >= 1) ? NodeProperties::GetValueInput(node, 2)
                                        : jsgraph()->UndefinedConstant();
            Node* key = (arity >= 2) ? NodeProperties::GetValueInput(node, 3)
                                     : jsgraph()->UndefinedConstant();
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Check whether {target} is a JSReceiver.
            Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
            Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

            // Throw an appropriate TypeError if the {target} is not a JSReceiver.
            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            Node* efalse = effect;
            {
                if_false = efalse = graph()->NewNode(
                    javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
                    jsgraph()->Constant(
                        static_cast<int>(MessageTemplate::kCalledOnNonObject)),
                    jsgraph()->HeapConstant(factory()->ReflectHas_string()), context,
                    frame_state, efalse, if_false);
            }

            // Otherwise just use the existing {JSHasProperty} logic.
            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            Node* etrue = effect;
            Node* vtrue;
            {
                // TODO(magardn): collect feedback so this can be optimized
                vtrue = etrue = if_true = graph()->NewNode(javascript()->HasProperty(VectorSlotPair()), target,
                    key, context, frame_state, etrue, if_true);
            }

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                // Create appropriate {IfException} and {IfSuccess} nodes.
                Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
                if_true = graph()->NewNode(common()->IfSuccess(), if_true);
                Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
                if_false = graph()->NewNode(common()->IfSuccess(), if_false);

                // Join the exception edges.
                Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
                Node* ephi = graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
                Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    extrue, exfalse, merge);
                ReplaceWithValue(on_exception, phi, ephi, merge);
            }

            // Connect the throwing path to end.
            if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
            NodeProperties::MergeControlToEnd(graph(), common(), if_false);

            // Continue on the regular path.
            ReplaceWithValue(node, vtrue, etrue, if_true);
            return Changed(vtrue);
        }

        Node* JSCallReducer::WireInLoopStart(Node* k, Node** control, Node** effect)
        {
            Node* loop = *control = graph()->NewNode(common()->Loop(2), *control, *control);
            Node* eloop = *effect = graph()->NewNode(common()->EffectPhi(2), *effect, *effect, loop);
            Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
            NodeProperties::MergeControlToEnd(graph(), common(), terminate);
            return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), k,
                k, loop);
        }

        void JSCallReducer::WireInLoopEnd(Node* loop, Node* eloop, Node* vloop, Node* k,
            Node* control, Node* effect)
        {
            loop->ReplaceInput(1, control);
            vloop->ReplaceInput(1, k);
            eloop->ReplaceInput(1, effect);
        }

        namespace {
            bool CanInlineArrayIteratingBuiltin(JSHeapBroker* broker,
                ZoneHandleSet<Map> receiver_maps,
                ElementsKind* kind_return)
            {
                DCHECK_NE(0, receiver_maps.size());
                *kind_return = MapRef(broker, receiver_maps[0]).elements_kind();
                for (auto receiver_map : receiver_maps) {
                    MapRef map(broker, receiver_map);
                    if (!map.supports_fast_array_iteration() || !UnionElementsKindUptoSize(kind_return, map.elements_kind())) {
                        return false;
                    }
                }
                return true;
            }

            bool CanInlineArrayResizingBuiltin(JSHeapBroker* broker,
                ZoneHandleSet<Map> receiver_maps,
                ElementsKind* kind_return,
                bool builtin_is_push = false)
            {
                DCHECK_NE(0, receiver_maps.size());
                *kind_return = MapRef(broker, receiver_maps[0]).elements_kind();
                for (auto receiver_map : receiver_maps) {
                    MapRef map(broker, receiver_map);
                    if (!map.supports_fast_array_resize())
                        return false;
                    if (builtin_is_push) {
                        if (!UnionElementsKindUptoPackedness(kind_return, map.elements_kind())) {
                            return false;
                        }
                    } else {
                        // TODO(turbofan): We should also handle fast holey double elements once
                        // we got the hole NaN mess sorted out in TurboFan/V8.
                        if (map.elements_kind() == HOLEY_DOUBLE_ELEMENTS || !UnionElementsKindUptoSize(kind_return, map.elements_kind())) {
                            return false;
                        }
                    }
                }
                return true;
            }
        } // namespace

        Reduction JSCallReducer::ReduceArrayForEach(
            Node* node, const SharedFunctionInfoRef& shared)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);

            // Try to determine the {receiver} map.
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* fncallback = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* this_arg = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (!dependencies()->DependOnNoElementsProtector())
                UNREACHABLE();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Node* k = jsgraph()->ZeroConstant();

            Node* original_length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                original_length };
            const int stack_parameters = arraysize(checkpoint_params);

            // Check whether the given callback function is callable. Note that this has
            // to happen outside the loop to make sure we also throw on empty arrays.
            Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared, Builtins::kArrayForEachLoopLazyDeoptContinuation,
                node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                outer_frame_state, ContinuationFrameStateMode::LAZY);
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
                &control, &check_fail, &check_throw);

            // Start the loop.
            Node* vloop = k = WireInLoopStart(k, &control, &effect);
            Node *loop = control, *eloop = effect;
            checkpoint_params[3] = k;

            Node* continue_test = graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
            Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone),
                continue_test, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
            Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
            control = if_true;

            Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared, Builtins::kArrayForEachLoopEagerDeoptContinuation,
                node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                outer_frame_state, ContinuationFrameStateMode::EAGER);

            effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);

            // Make sure the map hasn't changed during the iteration
            effect = graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
                                          receiver_maps, p.feedback()),
                receiver, effect, control);

            Node* element = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());

            Node* next_k = graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
            checkpoint_params[3] = next_k;

            Node* hole_true = nullptr;
            Node* hole_false = nullptr;
            Node* effect_true = effect;

            if (IsHoleyElementsKind(kind)) {
                // Holey elements kind require a hole check and skipping of the element in
                // the case of a hole.
                Node* check;
                if (IsDoubleElementsKind(kind)) {
                    check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
                } else {
                    check = graph()->NewNode(simplified()->ReferenceEqual(), element,
                        jsgraph()->TheHoleConstant());
                }
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
                hole_true = graph()->NewNode(common()->IfTrue(), branch);
                hole_false = graph()->NewNode(common()->IfFalse(), branch);
                control = hole_false;

                // The contract is that we don't leak "the hole" into "user JavaScript",
                // so we must rename the {element} here to explicitly exclude "the hole"
                // from the type of {element}.
                element = effect = graph()->NewNode(
                    common()->TypeGuard(Type::NonInternal()), element, effect, control);
            }

            frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared, Builtins::kArrayForEachLoopLazyDeoptContinuation,
                node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                outer_frame_state, ContinuationFrameStateMode::LAZY);

            control = effect = graph()->NewNode(
                javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
                receiver, context, frame_state, effect, control);

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
                    &check_fail, &control);
            }

            if (IsHoleyElementsKind(kind)) {
                Node* after_call_control = control;
                Node* after_call_effect = effect;
                control = hole_true;
                effect = effect_true;

                control = graph()->NewNode(common()->Merge(2), control, after_call_control);
                effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
                    control);
            }

            WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);

            control = if_false;
            effect = eloop;

            // Introduce proper LoopExit and LoopExitEffect nodes to mark
            // {loop} as a candidate for loop peeling (crbug.com/v8/8273).
            control = graph()->NewNode(common()->LoopExit(), control, loop);
            effect = graph()->NewNode(common()->LoopExitEffect(), effect, control);

            // Wire up the branch for the case when IsCallable fails for the callback.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the successful
            // completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
            return Replace(jsgraph()->UndefinedConstant());
        }

        Node* JSCallReducer::InsertMapChecksIfUnreliableReceiverMaps(
            NodeProperties::InferReceiverMapsResult result,
            ZoneHandleSet<Map> const& receiver_maps, VectorSlotPair const& feedback,
            Node* receiver, Node* effect, Node* control)
        {
            if (result == NodeProperties::kUnreliableReceiverMaps) {
                effect = graph()->NewNode(
                    simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps, feedback),
                    receiver, effect, control);
            }
            return effect;
        }

        Reduction JSCallReducer::ReduceArrayReduce(
            Node* node, ArrayReduceDirection direction,
            const SharedFunctionInfoRef& shared)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            bool left = direction == ArrayReduceDirection::kLeft;

            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);

            // Try to determine the {receiver} map.
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* fncallback = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();

            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            std::function<Node*(Node*)> hole_check = [this, kind](Node* element) {
                if (IsDoubleElementsKind(kind)) {
                    return graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
                } else {
                    return graph()->NewNode(simplified()->ReferenceEqual(), element,
                        jsgraph()->TheHoleConstant());
                }
            };

            if (!dependencies()->DependOnNoElementsProtector())
                UNREACHABLE();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Node* original_length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
                receiver, effect, control);

            Node* initial_index = left ? jsgraph()->ZeroConstant()
                                       : graph()->NewNode(simplified()->NumberSubtract(), original_length,
                                           jsgraph()->OneConstant());
            const Operator* next_op = left ? simplified()->NumberAdd() : simplified()->NumberSubtract();
            Node* k = initial_index;

            Node* check_frame_state;
            {
                Builtins::Name builtin_lazy = left ? Builtins::kArrayReduceLoopLazyDeoptContinuation
                                                   : Builtins::kArrayReduceRightLoopLazyDeoptContinuation;
                Node* checkpoint_params[] = { receiver, fncallback, k, original_length,
                    jsgraph()->UndefinedConstant() };
                const int stack_parameters = arraysize(checkpoint_params);
                check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, builtin_lazy, node->InputAt(0), context,
                    &checkpoint_params[0], stack_parameters - 1, outer_frame_state,
                    ContinuationFrameStateMode::LAZY);
            }
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            // Check whether the given callback function is callable. Note that
            // this has to happen outside the loop to make sure we also throw on
            // empty arrays.
            WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
                &control, &check_fail, &check_throw);

            // Set initial accumulator value
            Node* cur = jsgraph()->TheHoleConstant();

            if (node->op()->ValueInputCount() > 3) {
                cur = NodeProperties::GetValueInput(node, 3);
            } else {
                // Find first/last non holey element. In case the search fails, we need a
                // deopt continuation.
                Builtins::Name builtin_eager = left ? Builtins::kArrayReducePreLoopEagerDeoptContinuation
                                                    : Builtins::kArrayReduceRightPreLoopEagerDeoptContinuation;
                Node* checkpoint_params[] = { receiver, fncallback, original_length };
                const int stack_parameters = arraysize(checkpoint_params);
                Node* find_first_element_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, builtin_eager, node->InputAt(0), context,
                    &checkpoint_params[0], stack_parameters, outer_frame_state,
                    ContinuationFrameStateMode::EAGER);

                Node* vloop = k = WireInLoopStart(k, &control, &effect);
                Node* loop = control;
                Node* eloop = effect;
                effect = graph()->NewNode(common()->Checkpoint(),
                    find_first_element_frame_state, effect, control);
                Node* continue_test = left ? graph()->NewNode(simplified()->NumberLessThan(), k,
                                          original_length)
                                           : graph()->NewNode(simplified()->NumberLessThanOrEqual(),
                                               jsgraph()->ZeroConstant(), k);
                effect = graph()->NewNode(
                    simplified()->CheckIf(DeoptimizeReason::kNoInitialElement),
                    continue_test, effect, control);

                cur = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
                Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant());

                Node* hole_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
                    hole_check(cur), control);
                Node* found_el = graph()->NewNode(common()->IfFalse(), hole_branch);
                control = found_el;
                Node* is_hole = graph()->NewNode(common()->IfTrue(), hole_branch);

                WireInLoopEnd(loop, eloop, vloop, next_k, is_hole, effect);
                // We did the hole-check, so exclude hole from the type.
                cur = effect = graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
                    cur, effect, control);
                k = next_k;
            }

            // Start the loop.
            Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
            Node* eloop = effect = graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
            Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
            NodeProperties::MergeControlToEnd(graph(), common(), terminate);
            Node* kloop = k = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
            Node* curloop = cur = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2), cur, cur, loop);

            control = loop;
            effect = eloop;

            Node* continue_test = left
                ? graph()->NewNode(simplified()->NumberLessThan(), k, original_length)
                : graph()->NewNode(simplified()->NumberLessThanOrEqual(),
                    jsgraph()->ZeroConstant(), k);

            Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone),
                continue_test, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
            Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
            control = if_true;

            {
                Builtins::Name builtin_eager = left ? Builtins::kArrayReduceLoopEagerDeoptContinuation
                                                    : Builtins::kArrayReduceRightLoopEagerDeoptContinuation;
                Node* checkpoint_params[] = { receiver, fncallback, k, original_length,
                    curloop };
                const int stack_parameters = arraysize(checkpoint_params);
                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, builtin_eager, node->InputAt(0), context,
                    &checkpoint_params[0], stack_parameters, outer_frame_state,
                    ContinuationFrameStateMode::EAGER);
                effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
            }

            // Make sure the map hasn't changed during the iteration
            effect = graph()->NewNode(
                simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
                effect, control);

            Node* element = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());

            Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant());

            Node* hole_true = nullptr;
            Node* hole_false = nullptr;
            Node* effect_true = effect;

            if (IsHoleyElementsKind(kind)) {
                // Holey elements kind require a hole check and skipping of the element in
                // the case of a hole.
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
                    hole_check(element), control);
                hole_true = graph()->NewNode(common()->IfTrue(), branch);
                hole_false = graph()->NewNode(common()->IfFalse(), branch);
                control = hole_false;

                // The contract is that we don't leak "the hole" into "user JavaScript",
                // so we must rename the {element} here to explicitly exclude "the hole"
                // from the type of {element}.
                element = effect = graph()->NewNode(
                    common()->TypeGuard(Type::NonInternal()), element, effect, control);
            }

            Node* next_cur;
            {
                Builtins::Name builtin_lazy = left ? Builtins::kArrayReduceLoopLazyDeoptContinuation
                                                   : Builtins::kArrayReduceRightLoopLazyDeoptContinuation;
                Node* checkpoint_params[] = { receiver, fncallback, next_k, original_length,
                    curloop };
                const int stack_parameters = arraysize(checkpoint_params);
                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, builtin_lazy, node->InputAt(0), context,
                    &checkpoint_params[0], stack_parameters - 1, outer_frame_state,
                    ContinuationFrameStateMode::LAZY);

                next_cur = control = effect = graph()->NewNode(javascript()->Call(6, p.frequency()), fncallback,
                    jsgraph()->UndefinedConstant(), cur, element, k,
                    receiver, context, frame_state, effect, control);
            }

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
                    &check_fail, &control);
            }

            if (IsHoleyElementsKind(kind)) {
                Node* after_call_control = control;
                Node* after_call_effect = effect;
                control = hole_true;
                effect = effect_true;

                control = graph()->NewNode(common()->Merge(2), control, after_call_control);
                effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
                    control);
                next_cur = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), cur,
                    next_cur, control);
            }

            k = next_k;
            cur = next_cur;

            loop->ReplaceInput(1, control);
            kloop->ReplaceInput(1, k);
            curloop->ReplaceInput(1, cur);
            eloop->ReplaceInput(1, effect);

            control = if_false;
            effect = eloop;

            // Wire up the branch for the case when IsCallable fails for the callback.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the
            // successful completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, curloop, effect, control);
            return Replace(curloop);
        }

        Reduction JSCallReducer::ReduceArrayMap(Node* node,
            const SharedFunctionInfoRef& shared)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);

            // Try to determine the {receiver} map.
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* fncallback = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* this_arg = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (!dependencies()->DependOnArraySpeciesProtector())
                return NoChange();
            if (IsHoleyElementsKind(kind)) {
                if (!dependencies()->DependOnNoElementsProtector())
                    UNREACHABLE();
            }

            Node* array_constructor = jsgraph()->Constant(
                native_context().GetInitialJSArrayMap(kind).GetConstructor());

            Node* k = jsgraph()->ZeroConstant();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Node* original_length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            // If the array length >= kMaxFastArrayLength, then CreateArray
            // will create a dictionary. We should deopt in this case, and make sure
            // not to attempt inlining again.
            original_length = effect = graph()->NewNode(
                simplified()->CheckBounds(p.feedback()), original_length,
                jsgraph()->Constant(JSArray::kMaxFastArrayLength), effect, control);

            // Even though {JSCreateArray} is not marked as {kNoThrow}, we can elide the
            // exceptional projections because it cannot throw with the given
            // parameters.
            Node* a = control = effect = graph()->NewNode(
                javascript()->CreateArray(1, MaybeHandle<AllocationSite>()),
                array_constructor, array_constructor, original_length, context,
                outer_frame_state, effect, control);

            Node* checkpoint_params[] = { receiver, fncallback, this_arg,
                a, k, original_length };
            const int stack_parameters = arraysize(checkpoint_params);

            // Check whether the given callback function is callable. Note that this has
            // to happen outside the loop to make sure we also throw on empty arrays.
            Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared, Builtins::kArrayMapLoopLazyDeoptContinuation,
                node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                outer_frame_state, ContinuationFrameStateMode::LAZY);
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
                &control, &check_fail, &check_throw);

            // Start the loop.
            Node* vloop = k = WireInLoopStart(k, &control, &effect);
            Node *loop = control, *eloop = effect;
            checkpoint_params[4] = k;

            Node* continue_test = graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
            Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone),
                continue_test, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
            Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
            control = if_true;

            Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared, Builtins::kArrayMapLoopEagerDeoptContinuation,
                node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                outer_frame_state, ContinuationFrameStateMode::EAGER);

            effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);

            // Make sure the map hasn't changed during the iteration
            effect = graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
                                          receiver_maps, p.feedback()),
                receiver, effect, control);

            Node* element = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());

            Node* next_k = graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());

            Node* hole_true = nullptr;
            Node* hole_false = nullptr;
            Node* effect_true = effect;

            if (IsHoleyElementsKind(kind)) {
                // Holey elements kind require a hole check and skipping of the element in
                // the case of a hole.
                Node* check;
                if (IsDoubleElementsKind(kind)) {
                    check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
                } else {
                    check = graph()->NewNode(simplified()->ReferenceEqual(), element,
                        jsgraph()->TheHoleConstant());
                }
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
                hole_true = graph()->NewNode(common()->IfTrue(), branch);
                hole_false = graph()->NewNode(common()->IfFalse(), branch);
                control = hole_false;

                // The contract is that we don't leak "the hole" into "user JavaScript",
                // so we must rename the {element} here to explicitly exclude "the hole"
                // from the type of {element}.
                element = effect = graph()->NewNode(
                    common()->TypeGuard(Type::NonInternal()), element, effect, control);
            }

            // This frame state is dealt with by hand in
            // ArrayMapLoopLazyDeoptContinuation.
            frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared, Builtins::kArrayMapLoopLazyDeoptContinuation,
                node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                outer_frame_state, ContinuationFrameStateMode::LAZY);

            Node* callback_value = control = effect = graph()->NewNode(
                javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
                receiver, context, frame_state, effect, control);

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
                    &check_fail, &control);
            }

            // The array {a} should be HOLEY_SMI_ELEMENTS because we'd only come into
            // this loop if the input array length is non-zero, and "new Array({x > 0})"
            // always produces a HOLEY array.
            MapRef holey_double_map = native_context().GetInitialJSArrayMap(HOLEY_DOUBLE_ELEMENTS);
            MapRef holey_map = native_context().GetInitialJSArrayMap(HOLEY_ELEMENTS);
            effect = graph()->NewNode(simplified()->TransitionAndStoreElement(
                                          holey_double_map.object(), holey_map.object()),
                a, k, callback_value, effect, control);

            if (IsHoleyElementsKind(kind)) {
                Node* after_call_and_store_control = control;
                Node* after_call_and_store_effect = effect;
                control = hole_true;
                effect = effect_true;

                control = graph()->NewNode(common()->Merge(2), control,
                    after_call_and_store_control);
                effect = graph()->NewNode(common()->EffectPhi(2), effect,
                    after_call_and_store_effect, control);
            }

            WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);

            control = if_false;
            effect = eloop;

            // Wire up the branch for the case when IsCallable fails for the callback.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the
            // successful completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, a, effect, control);
            return Replace(a);
        }

        Reduction JSCallReducer::ReduceArrayFilter(
            Node* node, const SharedFunctionInfoRef& shared)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            // Try to determine the {receiver} map.
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* fncallback = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* this_arg = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            // The output array is packed (filter doesn't visit holes).
            const ElementsKind packed_kind = GetPackedElementsKind(kind);

            if (!dependencies()->DependOnArraySpeciesProtector())
                return NoChange();
            if (IsHoleyElementsKind(kind)) {
                if (!dependencies()->DependOnNoElementsProtector())
                    UNREACHABLE();
            }

            MapRef initial_map = native_context().GetInitialJSArrayMap(packed_kind);

            Node* k = jsgraph()->ZeroConstant();
            Node* to = jsgraph()->ZeroConstant();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Node* a; // Construct the output array.
            {
                AllocationBuilder ab(jsgraph(), effect, control);
                ab.Allocate(initial_map.instance_size(), AllocationType::kYoung,
                    Type::Array());
                ab.Store(AccessBuilder::ForMap(), initial_map);
                Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
                ab.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array);
                ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
                ab.Store(AccessBuilder::ForJSArrayLength(packed_kind),
                    jsgraph()->ZeroConstant());
                for (int i = 0; i < initial_map.GetInObjectProperties(); ++i) {
                    ab.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
                        jsgraph()->UndefinedConstant());
                }
                a = effect = ab.Finish();
            }

            Node* original_length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            // Check whether the given callback function is callable. Note that this has
            // to happen outside the loop to make sure we also throw on empty arrays.
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            {
                // This frame state doesn't ever call the deopt continuation, it's only
                // necessary to specifiy a continuation in order to handle the exceptional
                // case. We don't have all the values available to completely fill out
                // checkpoint_params yet, but that's okay because it'll never be called.
                // Therefore, "to" is mentioned twice, once standing in for the k_value
                // value.
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, a,
                    k, original_length, to, to };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::LAZY);
                WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
                    effect, &control, &check_fail, &check_throw);
            }

            // Start the loop.
            Node* vloop = k = WireInLoopStart(k, &control, &effect);
            Node *loop = control, *eloop = effect;
            Node* v_to_loop = to = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTaggedSigned, 2), to, to, loop);

            Node* continue_test = graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
            Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone),
                continue_test, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
            Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
            control = if_true;

            {
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, a,
                    k, original_length, to };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArrayFilterLoopEagerDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::EAGER);

                effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
            }

            // Make sure the map hasn't changed during the iteration.
            effect = graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
                                          receiver_maps, p.feedback()),
                receiver, effect, control);

            Node* element = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());

            Node* next_k = graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());

            Node* hole_true = nullptr;
            Node* hole_false = nullptr;
            Node* effect_true = effect;
            Node* hole_true_vto = to;

            if (IsHoleyElementsKind(kind)) {
                // Holey elements kind require a hole check and skipping of the element in
                // the case of a hole.
                Node* check;
                if (IsDoubleElementsKind(kind)) {
                    check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
                } else {
                    check = graph()->NewNode(simplified()->ReferenceEqual(), element,
                        jsgraph()->TheHoleConstant());
                }
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
                hole_true = graph()->NewNode(common()->IfTrue(), branch);
                hole_false = graph()->NewNode(common()->IfFalse(), branch);
                control = hole_false;

                // The contract is that we don't leak "the hole" into "user JavaScript",
                // so we must rename the {element} here to explicitly exclude "the hole"
                // from the type of {element}.
                element = effect = graph()->NewNode(
                    common()->TypeGuard(Type::NonInternal()), element, effect, control);
            }

            Node* callback_value = nullptr;
            {
                // This frame state is dealt with by hand in
                // Builtins::kArrayFilterLoopLazyDeoptContinuation.
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, a,
                    k, original_length, element, to };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::LAZY);

                callback_value = control = effect = graph()->NewNode(
                    javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
                    receiver, context, frame_state, effect, control);
            }

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
                    &check_fail, &control);
            }

            // We need an eager frame state for right after the callback function
            // returned, just in case an attempt to grow the output array fails.
            //
            // Note that we are intentionally reusing the
            // Builtins::kArrayFilterLoopLazyDeoptContinuation as an *eager* entry
            // point in this case. This is safe, because re-evaluating a [ToBoolean]
            // coercion is safe.
            {
                Node* checkpoint_params[] = { receiver, fncallback, this_arg,
                    a, k, original_length,
                    element, to, callback_value };
                const int stack_parameters = arraysize(checkpoint_params);
                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::EAGER);

                effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
            }

            // We have to coerce callback_value to boolean, and only store the element
            // in a if it's true. The checkpoint above protects against the case that
            // growing {a} fails.
            to = DoFilterPostCallbackWork(packed_kind, &control, &effect, a, to, element,
                callback_value);

            if (IsHoleyElementsKind(kind)) {
                Node* after_call_control = control;
                Node* after_call_effect = effect;
                control = hole_true;
                effect = effect_true;

                control = graph()->NewNode(common()->Merge(2), control, after_call_control);
                effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
                    control);
                to = graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2),
                    hole_true_vto, to, control);
            }

            WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
            v_to_loop->ReplaceInput(1, to);

            control = if_false;
            effect = eloop;

            // Wire up the branch for the case when IsCallable fails for the callback.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the
            // successful completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, a, effect, control);
            return Replace(a);
        }

        Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant,
            const SharedFunctionInfoRef& shared)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Builtins::Name eager_continuation_builtin;
            Builtins::Name lazy_continuation_builtin;
            Builtins::Name after_callback_lazy_continuation_builtin;
            if (variant == ArrayFindVariant::kFind) {
                eager_continuation_builtin = Builtins::kArrayFindLoopEagerDeoptContinuation;
                lazy_continuation_builtin = Builtins::kArrayFindLoopLazyDeoptContinuation;
                after_callback_lazy_continuation_builtin = Builtins::kArrayFindLoopAfterCallbackLazyDeoptContinuation;
            } else {
                DCHECK_EQ(ArrayFindVariant::kFindIndex, variant);
                eager_continuation_builtin = Builtins::kArrayFindIndexLoopEagerDeoptContinuation;
                lazy_continuation_builtin = Builtins::kArrayFindIndexLoopLazyDeoptContinuation;
                after_callback_lazy_continuation_builtin = Builtins::kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation;
            }

            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);

            // Try to determine the {receiver} map.
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* fncallback = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* this_arg = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (!dependencies()->DependOnNoElementsProtector())
                UNREACHABLE();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Node* k = jsgraph()->ZeroConstant();

            Node* original_length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                original_length };
            const int stack_parameters = arraysize(checkpoint_params);

            // Check whether the given callback function is callable. Note that this has
            // to happen outside the loop to make sure we also throw on empty arrays.
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            {
                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, lazy_continuation_builtin, node->InputAt(0), context,
                    &checkpoint_params[0], stack_parameters, outer_frame_state,
                    ContinuationFrameStateMode::LAZY);
                WireInCallbackIsCallableCheck(fncallback, context, frame_state, effect,
                    &control, &check_fail, &check_throw);
            }

            // Start the loop.
            Node* vloop = k = WireInLoopStart(k, &control, &effect);
            Node *loop = control, *eloop = effect;
            checkpoint_params[3] = k;

            // Check if we've iterated past the last element of the array.
            Node* if_false = nullptr;
            {
                Node* continue_test = graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
                Node* continue_branch = graph()->NewNode(
                    common()->Branch(BranchHint::kNone), continue_test, control);
                control = graph()->NewNode(common()->IfTrue(), continue_branch);
                if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
            }

            // Check the map hasn't changed during the iteration.
            {
                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, eager_continuation_builtin, node->InputAt(0),
                    context, &checkpoint_params[0], stack_parameters, outer_frame_state,
                    ContinuationFrameStateMode::EAGER);

                effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);

                effect = graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
                                              receiver_maps, p.feedback()),
                    receiver, effect, control);
            }

            // Load k-th element from receiver.
            Node* element = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());

            // Increment k for the next iteration.
            Node* next_k = checkpoint_params[3] = graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());

            // Replace holes with undefined.
            if (kind == HOLEY_DOUBLE_ELEMENTS) {
                // TODO(7409): avoid deopt if not all uses of value are truncated.
                CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
                element = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode, p.feedback()),
                    element, effect, control);
            } else if (IsHoleyElementsKind(kind)) {
                element = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), element);
            }

            Node* if_found_return_value = (variant == ArrayFindVariant::kFind) ? element : k;

            // Call the callback.
            Node* callback_value = nullptr;
            {
                Node* call_checkpoint_params[] = { receiver, fncallback,
                    this_arg, next_k,
                    original_length, if_found_return_value };
                const int call_stack_parameters = arraysize(call_checkpoint_params);

                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, after_callback_lazy_continuation_builtin,
                    node->InputAt(0), context, &call_checkpoint_params[0],
                    call_stack_parameters, outer_frame_state,
                    ContinuationFrameStateMode::LAZY);

                callback_value = control = effect = graph()->NewNode(
                    javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
                    receiver, context, frame_state, effect, control);
            }

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
                    &check_fail, &control);
            }

            // Check whether the given callback function returned a truthy value.
            Node* boolean_result = graph()->NewNode(simplified()->ToBoolean(), callback_value);
            Node* efound_branch = effect;
            Node* found_branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
                boolean_result, control);
            Node* if_found = graph()->NewNode(common()->IfTrue(), found_branch);
            Node* if_notfound = graph()->NewNode(common()->IfFalse(), found_branch);
            control = if_notfound;

            // Close the loop.
            WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);

            control = graph()->NewNode(common()->Merge(2), if_found, if_false);
            effect = graph()->NewNode(common()->EffectPhi(2), efound_branch, eloop, control);

            Node* if_not_found_value = (variant == ArrayFindVariant::kFind)
                ? jsgraph()->UndefinedConstant()
                : jsgraph()->MinusOneConstant();
            Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                if_found_return_value, if_not_found_value, control);

            // Introduce proper LoopExit/LoopExitEffect/LoopExitValue to mark
            // {loop} as a candidate for loop peeling (crbug.com/v8/8273).
            control = graph()->NewNode(common()->LoopExit(), control, loop);
            effect = graph()->NewNode(common()->LoopExitEffect(), effect, control);
            value = graph()->NewNode(common()->LoopExitValue(), value, control);

            // Wire up the branch for the case when IsCallable fails for the callback.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the
            // successful completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
            Node** effect, Node* a, Node* to,
            Node* element,
            Node* callback_value)
        {
            Node* boolean_result = graph()->NewNode(simplified()->ToBoolean(), callback_value);
            Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
                boolean_result, *control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), boolean_branch);
            Node* etrue = *effect;
            Node* vtrue;
            {
                // Load the elements backing store of the {receiver}.
                Node* elements = etrue = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSObjectElements()), a, etrue,
                    if_true);

                DCHECK(TypeCache::Get()->kFixedDoubleArrayLengthType.Is(
                    TypeCache::Get()->kFixedArrayLengthType));
                Node* checked_to = etrue = graph()->NewNode(
                    common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), to, etrue,
                    if_true);
                Node* elements_length = etrue = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
                    etrue, if_true);

                GrowFastElementsMode mode = IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
                                                                       : GrowFastElementsMode::kSmiOrObjectElements;
                elements = etrue = graph()->NewNode(
                    simplified()->MaybeGrowFastElements(mode, VectorSlotPair()), a,
                    elements, checked_to, elements_length, etrue, if_true);

                // Update the length of {a}.
                Node* new_length_a = graph()->NewNode(simplified()->NumberAdd(), checked_to,
                    jsgraph()->OneConstant());

                etrue = graph()->NewNode(
                    simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), a,
                    new_length_a, etrue, if_true);

                // Append the value to the {elements}.
                etrue = graph()->NewNode(
                    simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)),
                    elements, checked_to, element, etrue, if_true);

                vtrue = new_length_a;
            }

            Node* if_false = graph()->NewNode(common()->IfFalse(), boolean_branch);
            Node* efalse = *effect;
            Node* vfalse = to;

            *control = graph()->NewNode(common()->Merge(2), if_true, if_false);
            *effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, *control);
            to = graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2),
                vtrue, vfalse, *control);
            return to;
        }

        void JSCallReducer::WireInCallbackIsCallableCheck(
            Node* fncallback, Node* context, Node* check_frame_state, Node* effect,
            Node** control, Node** check_fail, Node** check_throw)
        {
            Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback);
            Node* check_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, *control);
            *check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
            *check_throw = *check_fail = graph()->NewNode(
                javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
                jsgraph()->Constant(
                    static_cast<int>(MessageTemplate::kCalledNonCallable)),
                fncallback, context, check_frame_state, effect, *check_fail);
            *control = graph()->NewNode(common()->IfTrue(), check_branch);
        }

        void JSCallReducer::RewirePostCallbackExceptionEdges(Node* check_throw,
            Node* on_exception,
            Node* effect,
            Node** check_fail,
            Node** control)
        {
            // Create appropriate {IfException} and {IfSuccess} nodes.
            Node* if_exception0 = graph()->NewNode(common()->IfException(), check_throw, *check_fail);
            *check_fail = graph()->NewNode(common()->IfSuccess(), *check_fail);
            Node* if_exception1 = graph()->NewNode(common()->IfException(), effect, *control);
            *control = graph()->NewNode(common()->IfSuccess(), *control);

            // Join the exception edges.
            Node* merge = graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
            Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
                if_exception1, merge);
            Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                if_exception0, if_exception1, merge);
            ReplaceWithValue(on_exception, phi, ephi, merge);
        }

        Node* JSCallReducer::SafeLoadElement(ElementsKind kind, Node* receiver,
            Node* control, Node** effect, Node** k,
            const VectorSlotPair& feedback)
        {
            // Make sure that the access is still in bounds, since the callback could
            // have changed the array's size.
            Node* length = *effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                *effect, control);
            *k = *effect = graph()->NewNode(simplified()->CheckBounds(feedback), *k,
                length, *effect, control);

            // Reload the elements pointer before calling the callback, since the
            // previous callback might have resized the array causing the elements
            // buffer to be re-allocated.
            Node* elements = *effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
                *effect, control);

            Node* element = *effect = graph()->NewNode(
                simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(
                    kind, LoadSensitivity::kCritical)),
                elements, *k, *effect, control);
            return element;
        }

        Reduction JSCallReducer::ReduceArrayEvery(Node* node,
            const SharedFunctionInfoRef& shared)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            // Try to determine the {receiver} map.
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* fncallback = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* this_arg = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (!dependencies()->DependOnArraySpeciesProtector())
                return NoChange();
            if (IsHoleyElementsKind(kind)) {
                if (!dependencies()->DependOnNoElementsProtector())
                    UNREACHABLE();
            }

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Node* k = jsgraph()->ZeroConstant();

            Node* original_length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            // Check whether the given callback function is callable. Note that this has
            // to happen outside the loop to make sure we also throw on empty arrays.
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            {
                // This frame state doesn't ever call the deopt continuation, it's only
                // necessary to specifiy a continuation in order to handle the exceptional
                // case.
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                    original_length };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArrayEveryLoopLazyDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::LAZY);
                WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
                    effect, &control, &check_fail, &check_throw);
            }

            // Start the loop.
            Node* vloop = k = WireInLoopStart(k, &control, &effect);
            Node *loop = control, *eloop = effect;

            Node* continue_test = graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
            Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone),
                continue_test, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
            Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
            control = if_true;

            {
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                    original_length };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArrayEveryLoopEagerDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::EAGER);

                effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
            }

            // Make sure the map hasn't changed during the iteration.
            effect = graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
                                          receiver_maps, p.feedback()),
                receiver, effect, control);

            Node* element = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());

            Node* next_k = graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());

            Node* hole_true = nullptr;
            Node* hole_false = nullptr;
            Node* effect_true = effect;

            if (IsHoleyElementsKind(kind)) {
                // Holey elements kind require a hole check and skipping of the element in
                // the case of a hole.
                Node* check;
                if (IsDoubleElementsKind(kind)) {
                    check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
                } else {
                    check = graph()->NewNode(simplified()->ReferenceEqual(), element,
                        jsgraph()->TheHoleConstant());
                }
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
                hole_true = graph()->NewNode(common()->IfTrue(), branch);
                hole_false = graph()->NewNode(common()->IfFalse(), branch);
                control = hole_false;

                // The contract is that we don't leak "the hole" into "user JavaScript",
                // so we must rename the {element} here to explicitly exclude "the hole"
                // from the type of {element}.
                element = effect = graph()->NewNode(
                    common()->TypeGuard(Type::NonInternal()), element, effect, control);
            }

            Node* callback_value = nullptr;
            {
                // This frame state is dealt with by hand in
                // Builtins::kArrayEveryLoopLazyDeoptContinuation.
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                    original_length };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArrayEveryLoopLazyDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::LAZY);

                callback_value = control = effect = graph()->NewNode(
                    javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
                    receiver, context, frame_state, effect, control);
            }

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
                    &check_fail, &control);
            }

            // We have to coerce callback_value to boolean.
            Node* if_false_callback;
            Node* efalse_callback;
            {
                Node* boolean_result = graph()->NewNode(simplified()->ToBoolean(), callback_value);
                Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
                    boolean_result, control);
                if_false_callback = graph()->NewNode(common()->IfFalse(), boolean_branch);
                efalse_callback = effect;

                // Nothing to do in the true case.
                control = graph()->NewNode(common()->IfTrue(), boolean_branch);
            }

            if (IsHoleyElementsKind(kind)) {
                Node* after_call_control = control;
                Node* after_call_effect = effect;
                control = hole_true;
                effect = effect_true;

                control = graph()->NewNode(common()->Merge(2), control, after_call_control);
                effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
                    control);
            }

            WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);

            control = graph()->NewNode(common()->Merge(2), if_false, if_false_callback);
            effect = graph()->NewNode(common()->EffectPhi(2), eloop, efalse_callback, control);
            Node* value = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2),
                jsgraph()->TrueConstant(), jsgraph()->FalseConstant(), control);

            // Introduce proper LoopExit/LoopExitEffect/LoopExitValue to mark
            // {loop} as a candidate for loop peeling (crbug.com/v8/8273).
            control = graph()->NewNode(common()->LoopExit(), control, loop);
            effect = graph()->NewNode(common()->LoopExitEffect(), effect, control);
            value = graph()->NewNode(common()->LoopExitValue(), value, control);

            // Wire up the branch for the case when IsCallable fails for the callback.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the
            // successful completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        namespace {

            // Returns the correct Callable for Array's indexOf based on the receiver's
            // |elements_kind| and |isolate|. Assumes that |elements_kind| is a fast one.
            Callable GetCallableForArrayIndexOf(ElementsKind elements_kind,
                Isolate* isolate)
            {
                switch (elements_kind) {
                case PACKED_SMI_ELEMENTS:
                case HOLEY_SMI_ELEMENTS:
                case PACKED_ELEMENTS:
                case HOLEY_ELEMENTS:
                    return Builtins::CallableFor(isolate, Builtins::kArrayIndexOfSmiOrObject);
                case PACKED_DOUBLE_ELEMENTS:
                    return Builtins::CallableFor(isolate,
                        Builtins::kArrayIndexOfPackedDoubles);
                default:
                    DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind);
                    return Builtins::CallableFor(isolate,
                        Builtins::kArrayIndexOfHoleyDoubles);
                }
            }

            // Returns the correct Callable for Array's includes based on the receiver's
            // |elements_kind| and |isolate|. Assumes that |elements_kind| is a fast one.
            Callable GetCallableForArrayIncludes(ElementsKind elements_kind,
                Isolate* isolate)
            {
                switch (elements_kind) {
                case PACKED_SMI_ELEMENTS:
                case HOLEY_SMI_ELEMENTS:
                case PACKED_ELEMENTS:
                case HOLEY_ELEMENTS:
                    return Builtins::CallableFor(isolate,
                        Builtins::kArrayIncludesSmiOrObject);
                case PACKED_DOUBLE_ELEMENTS:
                    return Builtins::CallableFor(isolate,
                        Builtins::kArrayIncludesPackedDoubles);
                default:
                    DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind);
                    return Builtins::CallableFor(isolate,
                        Builtins::kArrayIncludesHoleyDoubles);
                }
            }

        } // namespace

        // For search_variant == kIndexOf:
        // ES6 Array.prototype.indexOf(searchElement[, fromIndex])
        // #sec-array.prototype.indexof
        // For search_variant == kIncludes:
        // ES7 Array.prototype.inludes(searchElement[, fromIndex])
        // #sec-array.prototype.includes
        Reduction JSCallReducer::ReduceArrayIndexOfIncludes(
            SearchVariant search_variant, Node* node)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (IsHoleyElementsKind(kind)) {
                if (!dependencies()->DependOnNoElementsProtector())
                    UNREACHABLE();
            }

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Callable const callable = search_variant == SearchVariant::kIndexOf
                ? GetCallableForArrayIndexOf(kind, isolate())
                : GetCallableForArrayIncludes(kind, isolate());
            CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
                graph()->zone(), callable.descriptor(),
                callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
                Operator::kEliminatable);
            // The stub expects the following arguments: the receiver array, its
            // elements, the search_element, the array length, and the index to start
            // searching from.
            Node* elements = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
                effect, control);
            Node* search_element = (node->op()->ValueInputCount() >= 3)
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);
            Node* new_from_index = jsgraph()->ZeroConstant();
            if (node->op()->ValueInputCount() >= 4) {
                Node* from_index = NodeProperties::GetValueInput(node, 3);
                from_index = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
                    from_index, effect, control);
                // If the index is negative, it means the offset from the end and
                // therefore needs to be added to the length. If the result is still
                // negative, it needs to be clamped to 0.
                new_from_index = graph()->NewNode(
                    common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
                    graph()->NewNode(simplified()->NumberLessThan(), from_index,
                        jsgraph()->ZeroConstant()),
                    graph()->NewNode(
                        simplified()->NumberMax(),
                        graph()->NewNode(simplified()->NumberAdd(), length, from_index),
                        jsgraph()->ZeroConstant()),
                    from_index);
            }

            Node* context = NodeProperties::GetContextInput(node);
            Node* replacement_node = effect = graph()->NewNode(
                common()->Call(desc), jsgraph()->HeapConstant(callable.code()), elements,
                search_element, length, new_from_index, context, effect);
            ReplaceWithValue(node, replacement_node, effect);
            return Replace(replacement_node);
        }

        Reduction JSCallReducer::ReduceArraySome(Node* node,
            const SharedFunctionInfoRef& shared)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            // Try to determine the {receiver} map.
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* fncallback = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* this_arg = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            ElementsKind kind;
            if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (!dependencies()->DependOnArraySpeciesProtector())
                return NoChange();
            if (IsHoleyElementsKind(kind)) {
                if (!dependencies()->DependOnNoElementsProtector())
                    UNREACHABLE();
            }

            Node* k = jsgraph()->ZeroConstant();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            Node* original_length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            // Check whether the given callback function is callable. Note that this has
            // to happen outside the loop to make sure we also throw on empty arrays.
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            {
                // This frame state doesn't ever call the deopt continuation, it's only
                // necessary to specifiy a continuation in order to handle the exceptional
                // case.
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                    original_length };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArraySomeLoopLazyDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::LAZY);
                WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
                    effect, &control, &check_fail, &check_throw);
            }

            // Start the loop.
            Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
            Node* eloop = effect = graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
            Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
            NodeProperties::MergeControlToEnd(graph(), common(), terminate);
            Node* vloop = k = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);

            Node* continue_test = graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
            Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone),
                continue_test, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
            Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
            control = if_true;

            {
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                    original_length };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArraySomeLoopEagerDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::EAGER);

                effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
            }

            // Make sure the map hasn't changed during the iteration.
            effect = graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
                                          receiver_maps, p.feedback()),
                receiver, effect, control);

            Node* element = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());

            Node* next_k = graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());

            Node* hole_true = nullptr;
            Node* hole_false = nullptr;
            Node* effect_true = effect;

            if (IsHoleyElementsKind(kind)) {
                // Holey elements kind require a hole check and skipping of the element in
                // the case of a hole.
                Node* check;
                if (IsDoubleElementsKind(kind)) {
                    check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
                } else {
                    check = graph()->NewNode(simplified()->ReferenceEqual(), element,
                        jsgraph()->TheHoleConstant());
                }
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
                hole_true = graph()->NewNode(common()->IfTrue(), branch);
                hole_false = graph()->NewNode(common()->IfFalse(), branch);
                control = hole_false;

                // The contract is that we don't leak "the hole" into "user JavaScript",
                // so we must rename the {element} here to explicitly exclude "the hole"
                // from the type of {element}.
                element = effect = graph()->NewNode(
                    common()->TypeGuard(Type::NonInternal()), element, effect, control);
            }

            Node* callback_value = nullptr;
            {
                // This frame state is dealt with by hand in
                // Builtins::kArrayEveryLoopLazyDeoptContinuation.
                Node* checkpoint_params[] = { receiver, fncallback, this_arg, k,
                    original_length };
                const int stack_parameters = arraysize(checkpoint_params);

                Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                    jsgraph(), shared, Builtins::kArraySomeLoopLazyDeoptContinuation,
                    node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
                    outer_frame_state, ContinuationFrameStateMode::LAZY);

                callback_value = control = effect = graph()->NewNode(
                    javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
                    receiver, context, frame_state, effect, control);
            }

            // Rewire potential exception edges.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
                    &check_fail, &control);
            }

            // We have to coerce callback_value to boolean.
            Node* if_true_callback;
            Node* etrue_callback;
            {
                Node* boolean_result = graph()->NewNode(simplified()->ToBoolean(), callback_value);
                Node* boolean_branch = graph()->NewNode(
                    common()->Branch(BranchHint::kFalse), boolean_result, control);
                if_true_callback = graph()->NewNode(common()->IfTrue(), boolean_branch);
                etrue_callback = effect;

                // Nothing to do in the false case.
                control = graph()->NewNode(common()->IfFalse(), boolean_branch);
            }

            if (IsHoleyElementsKind(kind)) {
                Node* after_call_control = control;
                Node* after_call_effect = effect;
                control = hole_true;
                effect = effect_true;

                control = graph()->NewNode(common()->Merge(2), control, after_call_control);
                effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
                    control);
            }

            loop->ReplaceInput(1, control);
            vloop->ReplaceInput(1, next_k);
            eloop->ReplaceInput(1, effect);

            control = graph()->NewNode(common()->Merge(2), if_false, if_true_callback);
            effect = graph()->NewNode(common()->EffectPhi(2), eloop, etrue_callback, control);
            Node* value = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2),
                jsgraph()->FalseConstant(), jsgraph()->TrueConstant(), control);

            // Introduce proper LoopExit/LoopExitEffect/LoopExitValue to mark
            // {loop} as a candidate for loop peeling (crbug.com/v8/8273).
            control = graph()->NewNode(common()->LoopExit(), control, loop);
            effect = graph()->NewNode(common()->LoopExitEffect(), effect, control);
            value = graph()->NewNode(common()->LoopExitValue(), value, control);

            // Wire up the branch for the case when IsCallable fails for the callback.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the
            // successful completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        Reduction JSCallReducer::ReduceCallApiFunction(
            Node* node, const SharedFunctionInfoRef& shared)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            int const argc = static_cast<int>(p.arity()) - 2;
            Node* global_proxy = jsgraph()->Constant(native_context().global_proxy_object());
            Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined)
                ? global_proxy
                : NodeProperties::GetValueInput(node, 1);
            Node* holder;
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // See if we can optimize this API call to {shared}.
            Handle<FunctionTemplateInfo> function_template_info(
                FunctionTemplateInfo::cast(shared.object()->function_data()), isolate());
            CallOptimization call_optimization(isolate(), function_template_info);
            if (!call_optimization.is_simple_api_call())
                return NoChange();

            // Try to infer the {receiver} maps from the graph.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result != NodeProperties::kNoReceiverMaps) {
                // Check that all {receiver_maps} are actually JSReceiver maps and
                // that the {function_template_info} accepts them without access
                // checks (even if "access check needed" is set for {receiver}).
                //
                // Note that we don't need to know the concrete {receiver} maps here,
                // meaning it's fine if the {receiver_maps} are unreliable, and we also
                // don't need to install any stability dependencies, since the only
                // relevant information regarding the {receiver} is the Map::constructor
                // field on the root map (which is different from the JavaScript exposed
                // "constructor" property) and that field cannot change.
                //
                // So if we know that {receiver} had a certain constructor at some point
                // in the past (i.e. it had a certain map), then this constructor is going
                // to be the same later, since this information cannot change with map
                // transitions.
                //
                // The same is true for the instance type, e.g. we still know that the
                // instance type is JSObject even if that information is unreliable, and
                // the "access check needed" bit, which also cannot change later.
                for (Handle<Map> map : receiver_maps) {
                    MapRef receiver_map(broker(), map);
                    if (!receiver_map.IsJSReceiverMap() || (receiver_map.is_access_check_needed() && !function_template_info->accept_any_receiver())) {
                        return NoChange();
                    }
                }

                // See if we can constant-fold the compatible receiver checks.
                CallOptimization::HolderLookup lookup;
                Handle<JSObject> api_holder = call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup);
                if (lookup == CallOptimization::kHolderNotFound)
                    return NoChange();
                for (size_t i = 1; i < receiver_maps.size(); ++i) {
                    CallOptimization::HolderLookup lookupi;
                    Handle<JSObject> holderi = call_optimization.LookupHolderOfExpectedType(
                        receiver_maps[i], &lookupi);
                    if (lookup != lookupi)
                        return NoChange();
                    if (!api_holder.is_identical_to(holderi))
                        return NoChange();
                }

                // Determine the appropriate holder for the {lookup}.
                holder = lookup == CallOptimization::kHolderFound
                    ? jsgraph()->HeapConstant(api_holder)
                    : receiver;
            } else if (function_template_info->accept_any_receiver() && function_template_info->signature()->IsUndefined(isolate())) {
                // We haven't found any {receiver_maps}, but we might still be able to
                // optimize the API call depending on the {function_template_info}.
                // If the API function accepts any kind of {receiver}, we only need to
                // ensure that the {receiver} is actually a JSReceiver at this point,
                // and also pass that as the {holder}. There are two independent bits
                // here:
                //
                //  a. When the "accept any receiver" bit is set, it means we don't
                //     need to perform access checks, even if the {receiver}'s map
                //     has the "needs access check" bit set.
                //  b. When the {function_template_info} has no signature, we don't
                //     need to do the compatible receiver check, since all receivers
                //     are considered compatible at that point, and the {receiver}
                //     will be pass as the {holder}.
                //
                receiver = holder = effect = graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
                    receiver, global_proxy, effect, control);
            } else {
                // We don't have enough information to eliminate the access check
                // and/or the compatible receiver check, so use the generic builtin
                // that does those checks dynamically. This is still significantly
                // faster than the generic call sequence.
                Builtins::Name builtin_name = !function_template_info->accept_any_receiver()
                    ? (function_template_info->signature()->IsUndefined(isolate())
                            ? Builtins::kCallFunctionTemplate_CheckAccess
                            : Builtins::
                                kCallFunctionTemplate_CheckAccessAndCompatibleReceiver)
                    : Builtins::kCallFunctionTemplate_CheckCompatibleReceiver;

                // The CallFunctionTemplate builtin requires the {receiver} to be
                // an actual JSReceiver, so make sure we do the proper conversion
                // first if necessary.
                receiver = holder = effect = graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
                    receiver, global_proxy, effect, control);

                Callable callable = Builtins::CallableFor(isolate(), builtin_name);
                auto call_descriptor = Linkage::GetStubCallDescriptor(
                    graph()->zone(), callable.descriptor(),
                    argc + 1 /* implicit receiver */, CallDescriptor::kNeedsFrameState);
                node->InsertInput(graph()->zone(), 0,
                    jsgraph()->HeapConstant(callable.code()));
                node->ReplaceInput(1, jsgraph()->HeapConstant(function_template_info));
                node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc));
                node->ReplaceInput(3, receiver); // Update receiver input.
                node->ReplaceInput(6 + argc, effect); // Update effect input.
                NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
                return Changed(node);
            }

            // TODO(turbofan): Consider introducing a JSCallApiCallback operator for
            // this and lower it during JSGenericLowering, and unify this with the
            // JSNativeContextSpecialization::InlineApiCall method a bit.
            Handle<CallHandlerInfo> call_handler_info(
                CallHandlerInfo::cast(function_template_info->call_code()), isolate());
            Handle<Object> data(call_handler_info->data(), isolate());
            Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
            CallInterfaceDescriptor cid = call_api_callback.descriptor();
            auto call_descriptor = Linkage::GetStubCallDescriptor(
                graph()->zone(), cid, argc + 1 /* implicit receiver */,
                CallDescriptor::kNeedsFrameState);
            ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback()));
            ExternalReference function_reference = ExternalReference::Create(
                &api_function, ExternalReference::DIRECT_API_CALL);
            node->InsertInput(graph()->zone(), 0,
                jsgraph()->HeapConstant(call_api_callback.code()));
            node->ReplaceInput(1, jsgraph()->ExternalConstant(function_reference));
            node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc));
            node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(data));
            node->InsertInput(graph()->zone(), 4, holder);
            node->ReplaceInput(5, receiver); // Update receiver input.
            node->ReplaceInput(8 + argc, effect); // Update effect input.
            NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
            return Changed(node);
        }

        namespace {

            // Check whether elements aren't mutated; we play it extremely safe here by
            // explicitly checking that {node} is only used by {LoadField} or
            // {LoadElement}.
            bool IsSafeArgumentsElements(Node* node)
            {
                for (Edge const edge : node->use_edges()) {
                    if (!NodeProperties::IsValueEdge(edge))
                        continue;
                    if (edge.from()->opcode() != IrOpcode::kLoadField && edge.from()->opcode() != IrOpcode::kLoadElement) {
                        return false;
                    }
                }
                return true;
            }

        } // namespace

        Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
            Node* node, int arity, CallFrequency const& frequency,
            VectorSlotPair const& feedback)
        {
            DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || node->opcode() == IrOpcode::kJSCallWithSpread || node->opcode() == IrOpcode::kJSConstructWithArrayLike || node->opcode() == IrOpcode::kJSConstructWithSpread);

            // Check if {arguments_list} is an arguments object, and {node} is the only
            // value user of {arguments_list} (except for value uses in frame states).
            Node* arguments_list = NodeProperties::GetValueInput(node, arity);
            if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) {
                return NoChange();
            }
            for (Edge edge : arguments_list->use_edges()) {
                if (!NodeProperties::IsValueEdge(edge))
                    continue;
                Node* const user = edge.from();
                switch (user->opcode()) {
                case IrOpcode::kCheckMaps:
                case IrOpcode::kFrameState:
                case IrOpcode::kStateValues:
                case IrOpcode::kReferenceEqual:
                case IrOpcode::kReturn:
                    // Ignore safe uses that definitely don't mess with the arguments.
                    continue;
                case IrOpcode::kLoadField: {
                    DCHECK_EQ(arguments_list, user->InputAt(0));
                    FieldAccess const& access = FieldAccessOf(user->op());
                    if (access.offset == JSArray::kLengthOffset) {
                        // Ignore uses for arguments#length.
                        STATIC_ASSERT(
                            static_cast<int>(JSArray::kLengthOffset) == static_cast<int>(JSArgumentsObjectWithLength::kLengthOffset));
                        continue;
                    } else if (access.offset == JSObject::kElementsOffset) {
                        // Ignore safe uses for arguments#elements.
                        if (IsSafeArgumentsElements(user))
                            continue;
                    }
                    break;
                }
                case IrOpcode::kJSCallWithArrayLike:
                    // Ignore uses as argumentsList input to calls with array like.
                    if (user->InputAt(2) == arguments_list)
                        continue;
                    break;
                case IrOpcode::kJSConstructWithArrayLike:
                    // Ignore uses as argumentsList input to calls with array like.
                    if (user->InputAt(1) == arguments_list)
                        continue;
                    break;
                case IrOpcode::kJSCallWithSpread: {
                    // Ignore uses as spread input to calls with spread.
                    CallParameters p = CallParametersOf(user->op());
                    int const arity = static_cast<int>(p.arity() - 1);
                    if (user->InputAt(arity) == arguments_list)
                        continue;
                    break;
                }
                case IrOpcode::kJSConstructWithSpread: {
                    // Ignore uses as spread input to construct with spread.
                    ConstructParameters p = ConstructParametersOf(user->op());
                    int const arity = static_cast<int>(p.arity() - 2);
                    if (user->InputAt(arity) == arguments_list)
                        continue;
                    break;
                }
                default:
                    break;
                }
                // We cannot currently reduce the {node} to something better than what
                // it already is, but we might be able to do something about the {node}
                // later, so put it on the waitlist and try again during finalization.
                waitlist_.insert(node);
                return NoChange();
            }

            // Get to the actual frame state from which to extract the arguments;
            // we can only optimize this in case the {node} was already inlined into
            // some other function (and same for the {arguments_list}).
            CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op());
            Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list);
            FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
            int start_index = 0;

            int formal_parameter_count;
            {
                Handle<SharedFunctionInfo> shared;
                if (!state_info.shared_info().ToHandle(&shared))
                    return NoChange();
                formal_parameter_count = SharedFunctionInfoRef(broker(), shared)
                                             .internal_formal_parameter_count();
            }

            if (type == CreateArgumentsType::kMappedArguments) {
                // Mapped arguments (sloppy mode) that are aliased can only be handled
                // here if there's no side-effect between the {node} and the {arg_array}.
                // TODO(turbofan): Further relax this constraint.
                if (formal_parameter_count != 0) {
                    Node* effect = NodeProperties::GetEffectInput(node);
                    if (!NodeProperties::NoObservableSideEffectBetween(effect,
                            arguments_list)) {
                        return NoChange();
                    }
                }
            } else if (type == CreateArgumentsType::kRestParameter) {
                start_index = formal_parameter_count;
            }

            // For call/construct with spread, we need to also install a code
            // dependency on the array iterator lookup protector cell to ensure
            // that no one messed with the %ArrayIteratorPrototype%.next method.
            if (node->opcode() == IrOpcode::kJSCallWithSpread || node->opcode() == IrOpcode::kJSConstructWithSpread) {
                if (!dependencies()->DependOnArrayIteratorProtector())
                    return NoChange();
            }

            // Remove the {arguments_list} input from the {node}.
            node->RemoveInput(arity--);
            // Check if are spreading to inlined arguments or to the arguments of
            // the outermost function.
            Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
            if (outer_state->opcode() != IrOpcode::kFrameState) {
                Operator const* op = (node->opcode() == IrOpcode::kJSCallWithArrayLike || node->opcode() == IrOpcode::kJSCallWithSpread)
                    ? javascript()->CallForwardVarargs(arity + 1, start_index)
                    : javascript()->ConstructForwardVarargs(arity + 2, start_index);
                NodeProperties::ChangeOp(node, op);
                return Changed(node);
            }
            // Get to the actual frame state from which to extract the arguments;
            // we can only optimize this in case the {node} was already inlined into
            // some other function (and same for the {arg_array}).
            FrameStateInfo outer_info = FrameStateInfoOf(outer_state->op());
            if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
                // Need to take the parameters from the arguments adaptor.
                frame_state = outer_state;
            }
            // Add the actual parameters to the {node}, skipping the receiver.
            Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
            for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
                node->InsertInput(graph()->zone(), static_cast<int>(++arity),
                    parameters->InputAt(i));
            }

            if (node->opcode() == IrOpcode::kJSCallWithArrayLike || node->opcode() == IrOpcode::kJSCallWithSpread) {
                NodeProperties::ChangeOp(
                    node, javascript()->Call(arity + 1, frequency, feedback));
                Reduction const reduction = ReduceJSCall(node);
                return reduction.Changed() ? reduction : Changed(node);
            } else {
                NodeProperties::ChangeOp(
                    node, javascript()->Construct(arity + 2, frequency, feedback));
                Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
                Node* frame_state = NodeProperties::GetFrameStateInput(node);
                Node* context = NodeProperties::GetContextInput(node);
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);

                // Check whether the given new target value is a constructor function. The
                // replacement {JSConstruct} operator only checks the passed target value
                // but relies on the new target value to be implicitly valid.
                Node* check = graph()->NewNode(simplified()->ObjectIsConstructor(), new_target);
                Node* check_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
                Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
                Node* check_throw = check_fail = graph()->NewNode(
                    javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
                    jsgraph()->Constant(static_cast<int>(MessageTemplate::kNotConstructor)),
                    new_target, context, frame_state, effect, check_fail);
                control = graph()->NewNode(common()->IfTrue(), check_branch);
                NodeProperties::ReplaceControlInput(node, control);

                // Rewire potential exception edges.
                Node* on_exception = nullptr;
                if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                    // Create appropriate {IfException}  and {IfSuccess} nodes.
                    Node* if_exception = graph()->NewNode(common()->IfException(), check_throw, check_fail);
                    check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);

                    // Join the exception edges.
                    Node* merge = graph()->NewNode(common()->Merge(2), if_exception, on_exception);
                    Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception,
                        on_exception, merge);
                    Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                        if_exception, on_exception, merge);
                    ReplaceWithValue(on_exception, phi, ephi, merge);
                    merge->ReplaceInput(1, on_exception);
                    ephi->ReplaceInput(1, on_exception);
                    phi->ReplaceInput(1, on_exception);
                }

                // The above %ThrowTypeError runtime call is an unconditional throw,
                // making it impossible to return a successful completion in this case. We
                // simply connect the successful completion to the graph end.
                Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
                NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

                Reduction const reduction = ReduceJSConstruct(node);
                return reduction.Changed() ? reduction : Changed(node);
            }
        }

        namespace {

            bool ShouldUseCallICFeedback(Node* node)
            {
                HeapObjectMatcher m(node);
                if (m.HasValue() || m.IsJSCreateClosure()) {
                    // Don't use CallIC feedback when we know the function
                    // being called, i.e. either know the closure itself or
                    // at least the SharedFunctionInfo.
                    return false;
                } else if (m.IsPhi()) {
                    // Protect against endless loops here.
                    Node* control = NodeProperties::GetControlInput(node);
                    if (control->opcode() == IrOpcode::kLoop)
                        return false;
                    // Check if {node} is a Phi of nodes which shouldn't
                    // use CallIC feedback (not looking through loops).
                    int const value_input_count = m.node()->op()->ValueInputCount();
                    for (int n = 0; n < value_input_count; ++n) {
                        if (ShouldUseCallICFeedback(node->InputAt(n)))
                            return true;
                    }
                    return false;
                }
                return true;
            }

            base::Optional<HeapObjectRef> GetHeapObjectFeedback(
                JSHeapBroker* broker, const FeedbackNexus& nexus)
            {
                HeapObject object;
                if (!nexus.GetFeedback()->GetHeapObject(&object))
                    return base::nullopt;
                return HeapObjectRef(broker, handle(object, broker->isolate()));
            }

        } // namespace

        Reduction JSCallReducer::ReduceJSCall(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            Node* target = NodeProperties::GetValueInput(node, 0);
            Node* control = NodeProperties::GetControlInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            size_t arity = p.arity();
            DCHECK_LE(2u, arity);

            // Try to specialize JSCall {node}s with constant {target}s.
            HeapObjectMatcher m(target);
            if (m.HasValue()) {
                ObjectRef target_ref = m.Ref(broker());
                if (target_ref.IsJSFunction()) {
                    JSFunctionRef function = target_ref.AsJSFunction();
                    function.Serialize();

                    // Don't inline cross native context.
                    if (!function.native_context().equals(native_context())) {
                        return NoChange();
                    }

                    return ReduceJSCall(node, function.shared());
                } else if (target_ref.IsJSBoundFunction()) {
                    JSBoundFunctionRef function = target_ref.AsJSBoundFunction();
                    function.Serialize();

                    ObjectRef bound_this = function.bound_this();
                    ConvertReceiverMode const convert_mode = bound_this.IsNullOrUndefined()
                        ? ConvertReceiverMode::kNullOrUndefined
                        : ConvertReceiverMode::kNotNullOrUndefined;

                    // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]].
                    NodeProperties::ReplaceValueInput(
                        node, jsgraph()->Constant(function.bound_target_function()), 0);
                    NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this),
                        1);

                    // Insert the [[BoundArguments]] for {node}.
                    FixedArrayRef bound_arguments = function.bound_arguments();
                    for (int i = 0; i < bound_arguments.length(); ++i) {
                        node->InsertInput(graph()->zone(), i + 2,
                            jsgraph()->Constant(bound_arguments.get(i)));
                        arity++;
                    }

                    NodeProperties::ChangeOp(
                        node, javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));

                    // Try to further reduce the JSCall {node}.
                    Reduction const reduction = ReduceJSCall(node);
                    return reduction.Changed() ? reduction : Changed(node);
                }

                // Don't mess with other {node}s that have a constant {target}.
                // TODO(bmeurer): Also support proxies here.
                return NoChange();
            }

            // If {target} is the result of a JSCreateClosure operation, we can
            // just immediately try to inline based on the SharedFunctionInfo,
            // since TurboFan generally doesn't inline cross-context, and hence
            // the {target} must have the same native context as the call site.
            if (target->opcode() == IrOpcode::kJSCreateClosure) {
                CreateClosureParameters const& p = CreateClosureParametersOf(target->op());
                return ReduceJSCall(node, SharedFunctionInfoRef(broker(), p.shared_info()));
            }

            // If {target} is the result of a JSCreateBoundFunction operation,
            // we can just fold the construction and call the bound target
            // function directly instead.
            if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
                Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
                Node* bound_this = NodeProperties::GetValueInput(target, 1);
                int const bound_arguments_length = static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());

                // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]].
                NodeProperties::ReplaceValueInput(node, bound_target_function, 0);
                NodeProperties::ReplaceValueInput(node, bound_this, 1);

                // Insert the [[BoundArguments]] for {node}.
                for (int i = 0; i < bound_arguments_length; ++i) {
                    Node* value = NodeProperties::GetValueInput(target, 2 + i);
                    node->InsertInput(graph()->zone(), 2 + i, value);
                    arity++;
                }

                // Update the JSCall operator on {node}.
                ConvertReceiverMode const convert_mode = NodeProperties::CanBeNullOrUndefined(broker(), bound_this, effect)
                    ? ConvertReceiverMode::kAny
                    : ConvertReceiverMode::kNotNullOrUndefined;
                NodeProperties::ChangeOp(
                    node, javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));

                // Try to further reduce the JSCall {node}.
                Reduction const reduction = ReduceJSCall(node);
                return reduction.Changed() ? reduction : Changed(node);
            }

            // Extract feedback from the {node} using the FeedbackNexus.
            if (!p.feedback().IsValid())
                return NoChange();
            FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
            if (nexus.IsUninitialized()) {
                return ReduceSoftDeoptimize(
                    node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
            }

            base::Optional<HeapObjectRef> feedback = GetHeapObjectFeedback(broker(), nexus);
            if (feedback.has_value() && ShouldUseCallICFeedback(target) && feedback->map().is_callable()) {
                Node* target_function = jsgraph()->Constant(*feedback);

                // Check that the {target} is still the {target_function}.
                Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
                    target_function);
                effect = graph()->NewNode(
                    simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
                    effect, control);

                // Specialize the JSCall node to the {target_function}.
                NodeProperties::ReplaceValueInput(node, target_function, 0);
                NodeProperties::ReplaceEffectInput(node, effect);

                // Try to further reduce the JSCall {node}.
                Reduction const reduction = ReduceJSCall(node);
                return reduction.Changed() ? reduction : Changed(node);
            }

            return NoChange();
        }

        Reduction JSCallReducer::ReduceJSCall(Node* node,
            const SharedFunctionInfoRef& shared)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* target = NodeProperties::GetValueInput(node, 0);

            // Do not reduce calls to functions with break points.
            if (shared.HasBreakInfo())
                return NoChange();

            // Raise a TypeError if the {target} is a "classConstructor".
            if (IsClassConstructor(shared.kind())) {
                NodeProperties::ReplaceValueInputs(node, target);
                NodeProperties::ChangeOp(
                    node, javascript()->CallRuntime(Runtime::kThrowConstructorNonCallableError, 1));
                return Changed(node);
            }

            // Check for known builtin functions.

            int builtin_id = shared.HasBuiltinId() ? shared.builtin_id() : Builtins::kNoBuiltinId;
            switch (builtin_id) {
            case Builtins::kArrayConstructor:
                return ReduceArrayConstructor(node);
            case Builtins::kBooleanConstructor:
                return ReduceBooleanConstructor(node);
            case Builtins::kFunctionPrototypeApply:
                return ReduceFunctionPrototypeApply(node);
            case Builtins::kFastFunctionPrototypeBind:
                return ReduceFunctionPrototypeBind(node);
            case Builtins::kFunctionPrototypeCall:
                return ReduceFunctionPrototypeCall(node);
            case Builtins::kFunctionPrototypeHasInstance:
                return ReduceFunctionPrototypeHasInstance(node);
            case Builtins::kObjectConstructor:
                return ReduceObjectConstructor(node);
            case Builtins::kObjectCreate:
                return ReduceObjectCreate(node);
            case Builtins::kObjectGetPrototypeOf:
                return ReduceObjectGetPrototypeOf(node);
            case Builtins::kObjectIs:
                return ReduceObjectIs(node);
            case Builtins::kObjectPrototypeGetProto:
                return ReduceObjectPrototypeGetProto(node);
            case Builtins::kObjectPrototypeHasOwnProperty:
                return ReduceObjectPrototypeHasOwnProperty(node);
            case Builtins::kObjectPrototypeIsPrototypeOf:
                return ReduceObjectPrototypeIsPrototypeOf(node);
            case Builtins::kReflectApply:
                return ReduceReflectApply(node);
            case Builtins::kReflectConstruct:
                return ReduceReflectConstruct(node);
            case Builtins::kReflectGet:
                return ReduceReflectGet(node);
            case Builtins::kReflectGetPrototypeOf:
                return ReduceReflectGetPrototypeOf(node);
            case Builtins::kReflectHas:
                return ReduceReflectHas(node);
            case Builtins::kArrayForEach:
                return ReduceArrayForEach(node, shared);
            case Builtins::kArrayMap:
                return ReduceArrayMap(node, shared);
            case Builtins::kArrayFilter:
                return ReduceArrayFilter(node, shared);
            case Builtins::kArrayReduce:
                return ReduceArrayReduce(node, ArrayReduceDirection::kLeft, shared);
            case Builtins::kArrayReduceRight:
                return ReduceArrayReduce(node, ArrayReduceDirection::kRight, shared);
            case Builtins::kArrayPrototypeFind:
                return ReduceArrayFind(node, ArrayFindVariant::kFind, shared);
            case Builtins::kArrayPrototypeFindIndex:
                return ReduceArrayFind(node, ArrayFindVariant::kFindIndex, shared);
            case Builtins::kArrayEvery:
                return ReduceArrayEvery(node, shared);
            case Builtins::kArrayIndexOf:
                return ReduceArrayIndexOfIncludes(SearchVariant::kIndexOf, node);
            case Builtins::kArrayIncludes:
                return ReduceArrayIndexOfIncludes(SearchVariant::kIncludes, node);
            case Builtins::kArraySome:
                return ReduceArraySome(node, shared);
            case Builtins::kArrayPrototypePush:
                return ReduceArrayPrototypePush(node);
            case Builtins::kArrayPrototypePop:
                return ReduceArrayPrototypePop(node);
            case Builtins::kArrayPrototypeShift:
                return ReduceArrayPrototypeShift(node);
            case Builtins::kArrayPrototypeSlice:
                return ReduceArrayPrototypeSlice(node);
            case Builtins::kArrayPrototypeEntries:
                return ReduceArrayIterator(node, IterationKind::kEntries);
            case Builtins::kArrayPrototypeKeys:
                return ReduceArrayIterator(node, IterationKind::kKeys);
            case Builtins::kArrayPrototypeValues:
                return ReduceArrayIterator(node, IterationKind::kValues);
            case Builtins::kArrayIteratorPrototypeNext:
                return ReduceArrayIteratorPrototypeNext(node);
            case Builtins::kArrayIsArray:
                return ReduceArrayIsArray(node);
            case Builtins::kArrayBufferIsView:
                return ReduceArrayBufferIsView(node);
            case Builtins::kDataViewPrototypeGetByteLength:
                return ReduceArrayBufferViewAccessor(
                    node, JS_DATA_VIEW_TYPE,
                    AccessBuilder::ForJSArrayBufferViewByteLength());
            case Builtins::kDataViewPrototypeGetByteOffset:
                return ReduceArrayBufferViewAccessor(
                    node, JS_DATA_VIEW_TYPE,
                    AccessBuilder::ForJSArrayBufferViewByteOffset());
            case Builtins::kDataViewPrototypeGetUint8:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalUint8Array);
            case Builtins::kDataViewPrototypeGetInt8:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalInt8Array);
            case Builtins::kDataViewPrototypeGetUint16:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalUint16Array);
            case Builtins::kDataViewPrototypeGetInt16:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalInt16Array);
            case Builtins::kDataViewPrototypeGetUint32:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalUint32Array);
            case Builtins::kDataViewPrototypeGetInt32:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalInt32Array);
            case Builtins::kDataViewPrototypeGetFloat32:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalFloat32Array);
            case Builtins::kDataViewPrototypeGetFloat64:
                return ReduceDataViewAccess(node, DataViewAccess::kGet,
                    ExternalArrayType::kExternalFloat64Array);
            case Builtins::kDataViewPrototypeSetUint8:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalUint8Array);
            case Builtins::kDataViewPrototypeSetInt8:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalInt8Array);
            case Builtins::kDataViewPrototypeSetUint16:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalUint16Array);
            case Builtins::kDataViewPrototypeSetInt16:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalInt16Array);
            case Builtins::kDataViewPrototypeSetUint32:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalUint32Array);
            case Builtins::kDataViewPrototypeSetInt32:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalInt32Array);
            case Builtins::kDataViewPrototypeSetFloat32:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalFloat32Array);
            case Builtins::kDataViewPrototypeSetFloat64:
                return ReduceDataViewAccess(node, DataViewAccess::kSet,
                    ExternalArrayType::kExternalFloat64Array);
            case Builtins::kTypedArrayPrototypeByteLength:
                return ReduceArrayBufferViewAccessor(
                    node, JS_TYPED_ARRAY_TYPE,
                    AccessBuilder::ForJSArrayBufferViewByteLength());
            case Builtins::kTypedArrayPrototypeByteOffset:
                return ReduceArrayBufferViewAccessor(
                    node, JS_TYPED_ARRAY_TYPE,
                    AccessBuilder::ForJSArrayBufferViewByteOffset());
            case Builtins::kTypedArrayPrototypeLength:
                return ReduceArrayBufferViewAccessor(
                    node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength());
            case Builtins::kTypedArrayPrototypeToStringTag:
                return ReduceTypedArrayPrototypeToStringTag(node);
            case Builtins::kMathAbs:
                return ReduceMathUnary(node, simplified()->NumberAbs());
            case Builtins::kMathAcos:
                return ReduceMathUnary(node, simplified()->NumberAcos());
            case Builtins::kMathAcosh:
                return ReduceMathUnary(node, simplified()->NumberAcosh());
            case Builtins::kMathAsin:
                return ReduceMathUnary(node, simplified()->NumberAsin());
            case Builtins::kMathAsinh:
                return ReduceMathUnary(node, simplified()->NumberAsinh());
            case Builtins::kMathAtan:
                return ReduceMathUnary(node, simplified()->NumberAtan());
            case Builtins::kMathAtanh:
                return ReduceMathUnary(node, simplified()->NumberAtanh());
            case Builtins::kMathCbrt:
                return ReduceMathUnary(node, simplified()->NumberCbrt());
            case Builtins::kMathCeil:
                return ReduceMathUnary(node, simplified()->NumberCeil());
            case Builtins::kMathCos:
                return ReduceMathUnary(node, simplified()->NumberCos());
            case Builtins::kMathCosh:
                return ReduceMathUnary(node, simplified()->NumberCosh());
            case Builtins::kMathExp:
                return ReduceMathUnary(node, simplified()->NumberExp());
            case Builtins::kMathExpm1:
                return ReduceMathUnary(node, simplified()->NumberExpm1());
            case Builtins::kMathFloor:
                return ReduceMathUnary(node, simplified()->NumberFloor());
            case Builtins::kMathFround:
                return ReduceMathUnary(node, simplified()->NumberFround());
            case Builtins::kMathLog:
                return ReduceMathUnary(node, simplified()->NumberLog());
            case Builtins::kMathLog1p:
                return ReduceMathUnary(node, simplified()->NumberLog1p());
            case Builtins::kMathLog10:
                return ReduceMathUnary(node, simplified()->NumberLog10());
            case Builtins::kMathLog2:
                return ReduceMathUnary(node, simplified()->NumberLog2());
            case Builtins::kMathRound:
                return ReduceMathUnary(node, simplified()->NumberRound());
            case Builtins::kMathSign:
                return ReduceMathUnary(node, simplified()->NumberSign());
            case Builtins::kMathSin:
                return ReduceMathUnary(node, simplified()->NumberSin());
            case Builtins::kMathSinh:
                return ReduceMathUnary(node, simplified()->NumberSinh());
            case Builtins::kMathSqrt:
                return ReduceMathUnary(node, simplified()->NumberSqrt());
            case Builtins::kMathTan:
                return ReduceMathUnary(node, simplified()->NumberTan());
            case Builtins::kMathTanh:
                return ReduceMathUnary(node, simplified()->NumberTanh());
            case Builtins::kMathTrunc:
                return ReduceMathUnary(node, simplified()->NumberTrunc());
            case Builtins::kMathAtan2:
                return ReduceMathBinary(node, simplified()->NumberAtan2());
            case Builtins::kMathPow:
                return ReduceMathBinary(node, simplified()->NumberPow());
            case Builtins::kMathClz32:
                return ReduceMathClz32(node);
            case Builtins::kMathImul:
                return ReduceMathImul(node);
            case Builtins::kMathMax:
                return ReduceMathMinMax(node, simplified()->NumberMax(),
                    jsgraph()->Constant(-V8_INFINITY));
            case Builtins::kMathMin:
                return ReduceMathMinMax(node, simplified()->NumberMin(),
                    jsgraph()->Constant(V8_INFINITY));
            case Builtins::kNumberIsFinite:
                return ReduceNumberIsFinite(node);
            case Builtins::kNumberIsInteger:
                return ReduceNumberIsInteger(node);
            case Builtins::kNumberIsSafeInteger:
                return ReduceNumberIsSafeInteger(node);
            case Builtins::kNumberIsNaN:
                return ReduceNumberIsNaN(node);
            case Builtins::kNumberParseInt:
                return ReduceNumberParseInt(node);
            case Builtins::kGlobalIsFinite:
                return ReduceGlobalIsFinite(node);
            case Builtins::kGlobalIsNaN:
                return ReduceGlobalIsNaN(node);
            case Builtins::kMapPrototypeGet:
                return ReduceMapPrototypeGet(node);
            case Builtins::kMapPrototypeHas:
                return ReduceMapPrototypeHas(node);
            case Builtins::kRegExpPrototypeTest:
                return ReduceRegExpPrototypeTest(node);
            case Builtins::kReturnReceiver:
                return ReduceReturnReceiver(node);
            case Builtins::kStringPrototypeIndexOf:
                return ReduceStringPrototypeIndexOf(node);
            case Builtins::kStringPrototypeCharAt:
                return ReduceStringPrototypeCharAt(node);
            case Builtins::kStringPrototypeCharCodeAt:
                return ReduceStringPrototypeStringAt(simplified()->StringCharCodeAt(),
                    node);
            case Builtins::kStringPrototypeCodePointAt:
                return ReduceStringPrototypeStringAt(
                    simplified()->StringCodePointAt(UnicodeEncoding::UTF32), node);
            case Builtins::kStringPrototypeSubstring:
                return ReduceStringPrototypeSubstring(node);
            case Builtins::kStringPrototypeSlice:
                return ReduceStringPrototypeSlice(node);
            case Builtins::kStringPrototypeSubstr:
                return ReduceStringPrototypeSubstr(node);
#ifdef V8_INTL_SUPPORT
            case Builtins::kStringPrototypeToLowerCaseIntl:
                return ReduceStringPrototypeToLowerCaseIntl(node);
            case Builtins::kStringPrototypeToUpperCaseIntl:
                return ReduceStringPrototypeToUpperCaseIntl(node);
#endif // V8_INTL_SUPPORT
            case Builtins::kStringFromCharCode:
                return ReduceStringFromCharCode(node);
            case Builtins::kStringFromCodePoint:
                return ReduceStringFromCodePoint(node);
            case Builtins::kStringPrototypeIterator:
                return ReduceStringPrototypeIterator(node);
            case Builtins::kStringIteratorPrototypeNext:
                return ReduceStringIteratorPrototypeNext(node);
            case Builtins::kStringPrototypeConcat:
                return ReduceStringPrototypeConcat(node);
            case Builtins::kTypedArrayPrototypeEntries:
                return ReduceArrayIterator(node, IterationKind::kEntries);
            case Builtins::kTypedArrayPrototypeKeys:
                return ReduceArrayIterator(node, IterationKind::kKeys);
            case Builtins::kTypedArrayPrototypeValues:
                return ReduceArrayIterator(node, IterationKind::kValues);
            case Builtins::kPromiseInternalConstructor:
                return ReducePromiseInternalConstructor(node);
            case Builtins::kPromiseInternalReject:
                return ReducePromiseInternalReject(node);
            case Builtins::kPromiseInternalResolve:
                return ReducePromiseInternalResolve(node);
            case Builtins::kPromisePrototypeCatch:
                return ReducePromisePrototypeCatch(node);
            case Builtins::kPromisePrototypeFinally:
                return ReducePromisePrototypeFinally(node);
            case Builtins::kPromisePrototypeThen:
                return ReducePromisePrototypeThen(node);
            case Builtins::kPromiseResolveTrampoline:
                return ReducePromiseResolveTrampoline(node);
            case Builtins::kMapPrototypeEntries:
                return ReduceCollectionIteration(node, CollectionKind::kMap,
                    IterationKind::kEntries);
            case Builtins::kMapPrototypeKeys:
                return ReduceCollectionIteration(node, CollectionKind::kMap,
                    IterationKind::kKeys);
            case Builtins::kMapPrototypeGetSize:
                return ReduceCollectionPrototypeSize(node, CollectionKind::kMap);
            case Builtins::kMapPrototypeValues:
                return ReduceCollectionIteration(node, CollectionKind::kMap,
                    IterationKind::kValues);
            case Builtins::kMapIteratorPrototypeNext:
                return ReduceCollectionIteratorPrototypeNext(
                    node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(),
                    FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE);
            case Builtins::kSetPrototypeEntries:
                return ReduceCollectionIteration(node, CollectionKind::kSet,
                    IterationKind::kEntries);
            case Builtins::kSetPrototypeGetSize:
                return ReduceCollectionPrototypeSize(node, CollectionKind::kSet);
            case Builtins::kSetPrototypeValues:
                return ReduceCollectionIteration(node, CollectionKind::kSet,
                    IterationKind::kValues);
            case Builtins::kSetIteratorPrototypeNext:
                return ReduceCollectionIteratorPrototypeNext(
                    node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(),
                    FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE);
            case Builtins::kDatePrototypeGetTime:
                return ReduceDatePrototypeGetTime(node);
            case Builtins::kDateNow:
                return ReduceDateNow(node);
            case Builtins::kNumberConstructor:
                return ReduceNumberConstructor(node);
            default:
                break;
            }

            if (!TracingFlags::is_runtime_stats_enabled() && shared.object()->IsApiFunction()) {
                return ReduceCallApiFunction(node, shared);
            }
            return NoChange();
        }

        Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode());
            CallFrequency frequency = CallFrequencyOf(node->op());
            VectorSlotPair feedback;
            return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency,
                feedback);
        }

        Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            DCHECK_LE(3u, p.arity());
            int arity = static_cast<int>(p.arity() - 1);
            CallFrequency frequency = p.frequency();
            VectorSlotPair feedback = p.feedback();
            return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
                feedback);
        }

        Reduction JSCallReducer::ReduceJSConstruct(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
            ConstructParameters const& p = ConstructParametersOf(node->op());
            DCHECK_LE(2u, p.arity());
            int arity = static_cast<int>(p.arity() - 2);
            Node* target = NodeProperties::GetValueInput(node, 0);
            Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Extract feedback from the {node} using the FeedbackNexus.
            if (p.feedback().IsValid()) {
                FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
                if (nexus.IsUninitialized()) {
                    return ReduceSoftDeoptimize(
                        node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct);
                }

                base::Optional<HeapObjectRef> feedback = GetHeapObjectFeedback(broker(), nexus);
                if (feedback.has_value() && feedback->IsAllocationSite()) {
                    // The feedback is an AllocationSite, which means we have called the
                    // Array function and collected transition (and pretenuring) feedback
                    // for the resulting arrays.  This has to be kept in sync with the
                    // implementation in Ignition.

                    Node* array_function = jsgraph()->Constant(native_context().array_function());

                    // Check that the {target} is still the {array_function}.
                    Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
                        array_function);
                    effect = graph()->NewNode(
                        simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
                        effect, control);

                    // Turn the {node} into a {JSCreateArray} call.
                    NodeProperties::ReplaceEffectInput(node, effect);
                    for (int i = arity; i > 0; --i) {
                        NodeProperties::ReplaceValueInput(
                            node, NodeProperties::GetValueInput(node, i), i + 1);
                    }
                    NodeProperties::ReplaceValueInput(node, array_function, 1);
                    NodeProperties::ChangeOp(
                        node, javascript()->CreateArray(arity, feedback->AsAllocationSite().object()));
                    return Changed(node);
                } else if (feedback.has_value() && !HeapObjectMatcher(new_target).HasValue() && feedback->map().is_constructor()) {
                    Node* new_target_feedback = jsgraph()->Constant(*feedback);

                    // Check that the {new_target} is still the {new_target_feedback}.
                    Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target,
                        new_target_feedback);
                    effect = graph()->NewNode(
                        simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
                        effect, control);

                    // Specialize the JSConstruct node to the {new_target_feedback}.
                    NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1);
                    NodeProperties::ReplaceEffectInput(node, effect);
                    if (target == new_target) {
                        NodeProperties::ReplaceValueInput(node, new_target_feedback, 0);
                    }

                    // Try to further reduce the JSConstruct {node}.
                    Reduction const reduction = ReduceJSConstruct(node);
                    return reduction.Changed() ? reduction : Changed(node);
                }
            }

            // Try to specialize JSConstruct {node}s with constant {target}s.
            HeapObjectMatcher m(target);
            if (m.HasValue()) {
                HeapObjectRef target_ref = m.Ref(broker());

                // Raise a TypeError if the {target} is not a constructor.
                if (!target_ref.map().is_constructor()) {
                    NodeProperties::ReplaceValueInputs(node, target);
                    NodeProperties::ChangeOp(node,
                        javascript()->CallRuntime(
                            Runtime::kThrowConstructedNonConstructable));
                    return Changed(node);
                }

                if (target_ref.IsJSFunction()) {
                    JSFunctionRef function = target_ref.AsJSFunction();
                    function.Serialize();

                    // Do not reduce constructors with break points.
                    if (function.shared().HasBreakInfo())
                        return NoChange();

                    // Don't inline cross native context.
                    if (!function.native_context().equals(native_context())) {
                        return NoChange();
                    }

                    // Check for known builtin functions.
                    int builtin_id = function.shared().HasBuiltinId()
                        ? function.shared().builtin_id()
                        : Builtins::kNoBuiltinId;
                    switch (builtin_id) {
                    case Builtins::kArrayConstructor: {
                        // TODO(bmeurer): Deal with Array subclasses here.
                        // Turn the {node} into a {JSCreateArray} call.
                        for (int i = arity; i > 0; --i) {
                            NodeProperties::ReplaceValueInput(
                                node, NodeProperties::GetValueInput(node, i), i + 1);
                        }
                        NodeProperties::ReplaceValueInput(node, new_target, 1);
                        NodeProperties::ChangeOp(
                            node, javascript()->CreateArray(arity, Handle<AllocationSite>()));
                        return Changed(node);
                    }
                    case Builtins::kObjectConstructor: {
                        // If no value is passed, we can immediately lower to a simple
                        // JSCreate and don't need to do any massaging of the {node}.
                        if (arity == 0) {
                            NodeProperties::ChangeOp(node, javascript()->Create());
                            return Changed(node);
                        }

                        // Otherwise we can only lower to JSCreate if we know that
                        // the value parameter is ignored, which is only the case if
                        // the {new_target} and {target} are definitely not identical.
                        HeapObjectMatcher mnew_target(new_target);
                        if (mnew_target.HasValue() && !mnew_target.Ref(broker()).equals(function)) {
                            // Drop the value inputs.
                            for (int i = arity; i > 0; --i)
                                node->RemoveInput(i);
                            NodeProperties::ChangeOp(node, javascript()->Create());
                            return Changed(node);
                        }
                        break;
                    }
                    case Builtins::kPromiseConstructor:
                        return ReducePromiseConstructor(node);
                    case Builtins::kTypedArrayConstructor:
                        return ReduceTypedArrayConstructor(node, function.shared());
                    default:
                        break;
                    }
                } else if (target_ref.IsJSBoundFunction()) {
                    JSBoundFunctionRef function = target_ref.AsJSBoundFunction();
                    function.Serialize();

                    ObjectRef bound_target_function = function.bound_target_function();
                    FixedArrayRef bound_arguments = function.bound_arguments();

                    // Patch {node} to use [[BoundTargetFunction]].
                    NodeProperties::ReplaceValueInput(
                        node, jsgraph()->Constant(bound_target_function), 0);

                    // Patch {node} to use [[BoundTargetFunction]]
                    // as new.target if {new_target} equals {target}.
                    NodeProperties::ReplaceValueInput(
                        node,
                        graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
                            graph()->NewNode(simplified()->ReferenceEqual(),
                                target, new_target),
                            jsgraph()->Constant(bound_target_function),
                            new_target),
                        arity + 1);

                    // Insert the [[BoundArguments]] for {node}.
                    for (int i = 0; i < bound_arguments.length(); ++i) {
                        node->InsertInput(graph()->zone(), i + 1,
                            jsgraph()->Constant(bound_arguments.get(i)));
                        arity++;
                    }

                    // Update the JSConstruct operator on {node}.
                    NodeProperties::ChangeOp(
                        node,
                        javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair()));

                    // Try to further reduce the JSConstruct {node}.
                    Reduction const reduction = ReduceJSConstruct(node);
                    return reduction.Changed() ? reduction : Changed(node);
                }

                // TODO(bmeurer): Also support optimizing proxies here.
            }

            // If {target} is the result of a JSCreateBoundFunction operation,
            // we can just fold the construction and construct the bound target
            // function directly instead.
            if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
                Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
                int const bound_arguments_length = static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());

                // Patch the {node} to use [[BoundTargetFunction]].
                NodeProperties::ReplaceValueInput(node, bound_target_function, 0);

                // Patch {node} to use [[BoundTargetFunction]]
                // as new.target if {new_target} equals {target}.
                NodeProperties::ReplaceValueInput(
                    node,
                    graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
                        graph()->NewNode(simplified()->ReferenceEqual(),
                            target, new_target),
                        bound_target_function, new_target),
                    arity + 1);

                // Insert the [[BoundArguments]] for {node}.
                for (int i = 0; i < bound_arguments_length; ++i) {
                    Node* value = NodeProperties::GetValueInput(target, 2 + i);
                    node->InsertInput(graph()->zone(), 1 + i, value);
                    arity++;
                }

                // Update the JSConstruct operator on {node}.
                NodeProperties::ChangeOp(
                    node,
                    javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair()));

                // Try to further reduce the JSConstruct {node}.
                Reduction const reduction = ReduceJSConstruct(node);
                return reduction.Changed() ? reduction : Changed(node);
            }

            return NoChange();
        }

        // ES #sec-string.prototype.indexof
        Reduction JSCallReducer::ReduceStringPrototypeIndexOf(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            if (node->op()->ValueInputCount() >= 3) {
                Node* receiver = NodeProperties::GetValueInput(node, 1);
                Node* new_receiver = effect = graph()->NewNode(
                    simplified()->CheckString(p.feedback()), receiver, effect, control);

                Node* search_string = NodeProperties::GetValueInput(node, 2);
                Node* new_search_string = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), search_string,
                    effect, control);

                Node* new_position = jsgraph()->ZeroConstant();
                if (node->op()->ValueInputCount() >= 4) {
                    Node* position = NodeProperties::GetValueInput(node, 3);
                    new_position = effect = graph()->NewNode(
                        simplified()->CheckSmi(p.feedback()), position, effect, control);
                }

                NodeProperties::ReplaceEffectInput(node, effect);
                RelaxEffectsAndControls(node);
                node->ReplaceInput(0, new_receiver);
                node->ReplaceInput(1, new_search_string);
                node->ReplaceInput(2, new_position);
                node->TrimInputCount(3);
                NodeProperties::ChangeOp(node, simplified()->StringIndexOf());
                return Changed(node);
            }
            return NoChange();
        }

        // ES #sec-string.prototype.substring
        Reduction JSCallReducer::ReduceStringPrototypeSubstring(Node* node)
        {
            if (node->op()->ValueInputCount() < 3)
                return NoChange();
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* start = NodeProperties::GetValueInput(node, 2);
            Node* end = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();

            receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                receiver, effect, control);

            start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start,
                effect, control);

            Node* length = graph()->NewNode(simplified()->StringLength(), receiver);

            Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end,
                jsgraph()->UndefinedConstant());
            Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            Node* etrue = effect;
            Node* vtrue = length;

            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            Node* efalse = effect;
            Node* vfalse = efalse = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
                end, efalse, if_false);

            control = graph()->NewNode(common()->Merge(2), if_true, if_false);
            effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
            end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                vtrue, vfalse, control);
            Node* finalStart = graph()->NewNode(simplified()->NumberMin(),
                graph()->NewNode(simplified()->NumberMax(), start,
                    jsgraph()->ZeroConstant()),
                length);
            Node* finalEnd = graph()->NewNode(simplified()->NumberMin(),
                graph()->NewNode(simplified()->NumberMax(), end,
                    jsgraph()->ZeroConstant()),
                length);

            Node* from = graph()->NewNode(simplified()->NumberMin(), finalStart, finalEnd);
            Node* to = graph()->NewNode(simplified()->NumberMax(), finalStart, finalEnd);

            Node* value = effect = graph()->NewNode(simplified()->StringSubstring(),
                receiver, from, to, effect, control);
            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES #sec-string.prototype.slice
        Reduction JSCallReducer::ReduceStringPrototypeSlice(Node* node)
        {
            if (node->op()->ValueInputCount() < 3)
                return NoChange();
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* start = NodeProperties::GetValueInput(node, 2);
            Node* end = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();

            receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                receiver, effect, control);

            start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start,
                effect, control);

            Node* length = graph()->NewNode(simplified()->StringLength(), receiver);

            // Replace {end} argument with {length} if it is undefined.
            {
                Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end,
                    jsgraph()->UndefinedConstant());

                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

                Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
                Node* etrue = effect;
                Node* vtrue = length;

                Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                Node* efalse = effect;
                Node* vfalse = efalse = graph()->NewNode(
                    simplified()->CheckSmi(p.feedback()), end, efalse, if_false);

                control = graph()->NewNode(common()->Merge(2), if_true, if_false);
                effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
                end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    vtrue, vfalse, control);
            }

            Node* from = graph()->NewNode(
                common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
                graph()->NewNode(simplified()->NumberLessThan(), start,
                    jsgraph()->ZeroConstant()),
                graph()->NewNode(
                    simplified()->NumberMax(),
                    graph()->NewNode(simplified()->NumberAdd(), length, start),
                    jsgraph()->ZeroConstant()),
                graph()->NewNode(simplified()->NumberMin(), start, length));
            // {from} is always in non-negative Smi range, but our typer cannot
            // figure that out yet.
            from = effect = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()),
                from, effect, control);

            Node* to = graph()->NewNode(
                common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
                graph()->NewNode(simplified()->NumberLessThan(), end,
                    jsgraph()->ZeroConstant()),
                graph()->NewNode(simplified()->NumberMax(),
                    graph()->NewNode(simplified()->NumberAdd(), length, end),
                    jsgraph()->ZeroConstant()),
                graph()->NewNode(simplified()->NumberMin(), end, length));
            // {to} is always in non-negative Smi range, but our typer cannot
            // figure that out yet.
            to = effect = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), to,
                effect, control);

            Node* result_string = nullptr;
            // Return empty string if {from} is smaller than {to}.
            {
                Node* check = graph()->NewNode(simplified()->NumberLessThan(), from, to);

                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

                Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
                Node* etrue = effect;
                Node* vtrue = etrue = graph()->NewNode(simplified()->StringSubstring(),
                    receiver, from, to, etrue, if_true);

                Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                Node* efalse = effect;
                Node* vfalse = jsgraph()->EmptyStringConstant();

                control = graph()->NewNode(common()->Merge(2), if_true, if_false);
                effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
                result_string = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    vtrue, vfalse, control);
            }

            ReplaceWithValue(node, result_string, effect, control);
            return Replace(result_string);
        }

        // ES #sec-string.prototype.substr
        Reduction JSCallReducer::ReduceStringPrototypeSubstr(Node* node)
        {
            if (node->op()->ValueInputCount() < 3)
                return NoChange();
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* start = NodeProperties::GetValueInput(node, 2);
            Node* end = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();

            receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                receiver, effect, control);

            start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start,
                effect, control);

            Node* length = graph()->NewNode(simplified()->StringLength(), receiver);

            // Replace {end} argument with {length} if it is undefined.
            {
                Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end,
                    jsgraph()->UndefinedConstant());
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

                Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
                Node* etrue = effect;
                Node* vtrue = length;

                Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                Node* efalse = effect;
                Node* vfalse = efalse = graph()->NewNode(
                    simplified()->CheckSmi(p.feedback()), end, efalse, if_false);

                control = graph()->NewNode(common()->Merge(2), if_true, if_false);
                effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
                end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    vtrue, vfalse, control);
            }

            Node* initStart = graph()->NewNode(
                common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
                graph()->NewNode(simplified()->NumberLessThan(), start,
                    jsgraph()->ZeroConstant()),
                graph()->NewNode(
                    simplified()->NumberMax(),
                    graph()->NewNode(simplified()->NumberAdd(), length, start),
                    jsgraph()->ZeroConstant()),
                start);
            // The select above guarantees that initStart is non-negative, but
            // our typer can't figure that out yet.
            initStart = effect = graph()->NewNode(
                common()->TypeGuard(Type::UnsignedSmall()), initStart, effect, control);

            Node* resultLength = graph()->NewNode(
                simplified()->NumberMin(),
                graph()->NewNode(simplified()->NumberMax(), end,
                    jsgraph()->ZeroConstant()),
                graph()->NewNode(simplified()->NumberSubtract(), length, initStart));

            // The the select below uses {resultLength} only if {resultLength > 0},
            // but our typer can't figure that out yet.
            Node* to = effect = graph()->NewNode(
                common()->TypeGuard(Type::UnsignedSmall()),
                graph()->NewNode(simplified()->NumberAdd(), initStart, resultLength),
                effect, control);

            Node* result_string = nullptr;
            // Return empty string if {from} is smaller than {to}.
            {
                Node* check = graph()->NewNode(simplified()->NumberLessThan(),
                    jsgraph()->ZeroConstant(), resultLength);

                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

                Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
                Node* etrue = effect;
                Node* vtrue = etrue = graph()->NewNode(simplified()->StringSubstring(), receiver, initStart,
                    to, etrue, if_true);

                Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                Node* efalse = effect;
                Node* vfalse = jsgraph()->EmptyStringConstant();

                control = graph()->NewNode(common()->Merge(2), if_true, if_false);
                effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
                result_string = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    vtrue, vfalse, control);
            }

            ReplaceWithValue(node, result_string, effect, control);
            return Replace(result_string);
        }

        Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode());
            CallFrequency frequency = CallFrequencyOf(node->op());
            VectorSlotPair feedback;
            return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency,
                feedback);
        }

        Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode());
            ConstructParameters const& p = ConstructParametersOf(node->op());
            DCHECK_LE(3u, p.arity());
            int arity = static_cast<int>(p.arity() - 2);
            CallFrequency frequency = p.frequency();
            VectorSlotPair feedback = p.feedback();
            return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
                feedback);
        }

        Reduction JSCallReducer::ReduceReturnReceiver(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            ReplaceWithValue(node, receiver);
            return Replace(receiver);
        }

        Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node,
            DeoptimizeReason reason)
        {
            if (flags() & kBailoutOnUninitialized) {
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);
                Node* frame_state = NodeProperties::FindFrameStateBefore(node);
                Node* deoptimize = graph()->NewNode(
                    common()->Deoptimize(DeoptimizeKind::kSoft, reason, VectorSlotPair()),
                    frame_state, effect, control);
                // TODO(bmeurer): This should be on the AdvancedReducer somehow.
                NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
                Revisit(graph()->end());
                node->TrimInputCount(0);
                NodeProperties::ChangeOp(node, common()->Dead());
                return Changed(node);
            }
            return NoChange();
        }

        // ES6 section 22.1.3.18 Array.prototype.push ( )
        Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            int const num_values = node->op()->ValueInputCount() - 2;
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());

            ElementsKind kind;
            if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind, true)) {
                return NoChange();
            }

            if (!dependencies()->DependOnNoElementsProtector())
                UNREACHABLE();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            // Collect the value inputs to push.
            std::vector<Node*> values(num_values);
            for (int i = 0; i < num_values; ++i) {
                values[i] = NodeProperties::GetValueInput(node, 2 + i);
            }

            for (auto& value : values) {
                if (IsSmiElementsKind(kind)) {
                    value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
                        value, effect, control);
                } else if (IsDoubleElementsKind(kind)) {
                    value = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()),
                        value, effect, control);
                    // Make sure we do not store signaling NaNs into double arrays.
                    value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
                }
            }

            // Load the "length" property of the {receiver}.
            Node* length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);
            Node* value = length;

            // Check if we have any {values} to push.
            if (num_values > 0) {
                // Compute the resulting "length" of the {receiver}.
                Node* new_length = value = graph()->NewNode(
                    simplified()->NumberAdd(), length, jsgraph()->Constant(num_values));

                // Load the elements backing store of the {receiver}.
                Node* elements = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
                    effect, control);
                Node* elements_length = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
                    effect, control);

                GrowFastElementsMode mode = IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
                                                                       : GrowFastElementsMode::kSmiOrObjectElements;
                elements = effect = graph()->NewNode(
                    simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver,
                    elements,
                    graph()->NewNode(simplified()->NumberAdd(), length,
                        jsgraph()->Constant(num_values - 1)),
                    elements_length, effect, control);

                // Update the JSArray::length field. Since this is observable,
                // there must be no other check after this.
                effect = graph()->NewNode(
                    simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
                    receiver, new_length, effect, control);

                // Append the {values} to the {elements}.
                for (int i = 0; i < num_values; ++i) {
                    Node* value = values[i];
                    Node* index = graph()->NewNode(simplified()->NumberAdd(), length,
                        jsgraph()->Constant(i));
                    effect = graph()->NewNode(
                        simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)),
                        elements, index, value, effect, control);
                }
            }

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES6 section 22.1.3.17 Array.prototype.pop ( )
        Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());

            ElementsKind kind;
            if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (!dependencies()->DependOnNoElementsProtector())
                UNREACHABLE();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            // Load the "length" property of the {receiver}.
            Node* length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            // Check if the {receiver} has any elements.
            Node* check = graph()->NewNode(simplified()->NumberEqual(), length,
                jsgraph()->ZeroConstant());
            Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            Node* etrue = effect;
            Node* vtrue = jsgraph()->UndefinedConstant();

            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            Node* efalse = effect;
            Node* vfalse;
            {
                // TODO(tebbi): We should trim the backing store if the capacity is too
                // big, as implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.

                // Load the elements backing store from the {receiver}.
                Node* elements = efalse = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
                    efalse, if_false);

                // Ensure that we aren't popping from a copy-on-write backing store.
                if (IsSmiOrObjectElementsKind(kind)) {
                    elements = efalse = graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver,
                        elements, efalse, if_false);
                }

                // Compute the new {length}.
                length = graph()->NewNode(simplified()->NumberSubtract(), length,
                    jsgraph()->OneConstant());

                // Store the new {length} to the {receiver}.
                efalse = graph()->NewNode(
                    simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
                    receiver, length, efalse, if_false);

                // Load the last entry from the {elements}.
                vfalse = efalse = graph()->NewNode(
                    simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
                    elements, length, efalse, if_false);

                // Store a hole to the element we just removed from the {receiver}.
                efalse = graph()->NewNode(
                    simplified()->StoreElement(
                        AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))),
                    elements, length, jsgraph()->TheHoleConstant(), efalse, if_false);
            }

            control = graph()->NewNode(common()->Merge(2), if_true, if_false);
            effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
            Node* value = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);

            // Convert the hole to undefined. Do this last, so that we can optimize
            // conversion operator via some smart strength reduction in many cases.
            if (IsHoleyElementsKind(kind)) {
                value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
            }

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES6 section 22.1.3.22 Array.prototype.shift ( )
        Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* target = NodeProperties::GetValueInput(node, 0);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());

            ElementsKind kind;
            if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind)) {
                return NoChange();
            }

            if (!dependencies()->DependOnNoElementsProtector())
                UNREACHABLE();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            // Load length of the {receiver}.
            Node* length = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
                effect, control);

            // Return undefined if {receiver} has no elements.
            Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length,
                jsgraph()->ZeroConstant());
            Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);

            Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
            Node* etrue0 = effect;
            Node* vtrue0 = jsgraph()->UndefinedConstant();

            Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
            Node* efalse0 = effect;
            Node* vfalse0;
            {
                // Check if we should take the fast-path.
                Node* check1 = graph()->NewNode(simplified()->NumberLessThanOrEqual(), length,
                    jsgraph()->Constant(JSArray::kMaxCopyElements));
                Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
                    check1, if_false0);

                Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
                Node* etrue1 = efalse0;
                Node* vtrue1;
                {
                    Node* elements = etrue1 = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
                        receiver, etrue1, if_true1);

                    // Load the first element here, which we return below.
                    vtrue1 = etrue1 = graph()->NewNode(
                        simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
                        elements, jsgraph()->ZeroConstant(), etrue1, if_true1);

                    // Ensure that we aren't shifting a copy-on-write backing store.
                    if (IsSmiOrObjectElementsKind(kind)) {
                        elements = etrue1 = graph()->NewNode(simplified()->EnsureWritableFastElements(),
                            receiver, elements, etrue1, if_true1);
                    }

                    // Shift the remaining {elements} by one towards the start.
                    Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1);
                    Node* eloop = graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop);
                    Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
                    NodeProperties::MergeControlToEnd(graph(), common(), terminate);
                    Node* index = graph()->NewNode(
                        common()->Phi(MachineRepresentation::kTagged, 2),
                        jsgraph()->OneConstant(),
                        jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop);

                    {
                        Node* check2 = graph()->NewNode(simplified()->NumberLessThan(), index, length);
                        Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop);

                        if_true1 = graph()->NewNode(common()->IfFalse(), branch2);
                        etrue1 = eloop;

                        Node* control = graph()->NewNode(common()->IfTrue(), branch2);
                        Node* effect = etrue1;

                        ElementAccess const access = AccessBuilder::ForFixedArrayElement(kind);
                        Node* value = effect = graph()->NewNode(simplified()->LoadElement(access), elements, index,
                            effect, control);
                        effect = graph()->NewNode(simplified()->StoreElement(access), elements,
                            graph()->NewNode(simplified()->NumberSubtract(),
                                index, jsgraph()->OneConstant()),
                            value, effect, control);

                        loop->ReplaceInput(1, control);
                        eloop->ReplaceInput(1, effect);
                        index->ReplaceInput(1,
                            graph()->NewNode(simplified()->NumberAdd(), index,
                                jsgraph()->OneConstant()));
                    }

                    // Compute the new {length}.
                    length = graph()->NewNode(simplified()->NumberSubtract(), length,
                        jsgraph()->OneConstant());

                    // Store the new {length} to the {receiver}.
                    etrue1 = graph()->NewNode(
                        simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
                        receiver, length, etrue1, if_true1);

                    // Store a hole to the element we just removed from the {receiver}.
                    etrue1 = graph()->NewNode(
                        simplified()->StoreElement(
                            AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))),
                        elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1);
                }

                Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
                Node* efalse1 = efalse0;
                Node* vfalse1;
                {
                    // Call the generic C++ implementation.
                    const int builtin_index = Builtins::kArrayShift;
                    auto call_descriptor = Linkage::GetCEntryStubCallDescriptor(
                        graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver,
                        Builtins::name(builtin_index), node->op()->properties(),
                        CallDescriptor::kNeedsFrameState);
                    Node* stub_code = jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, kArgvOnStack, true);
                    Address builtin_entry = Builtins::CppEntryOf(builtin_index);
                    Node* entry = jsgraph()->ExternalConstant(ExternalReference::Create(builtin_entry));
                    Node* argc = jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver);
                    if_false1 = efalse1 = vfalse1 = graph()->NewNode(common()->Call(call_descriptor), stub_code, receiver,
                        jsgraph()->PaddingConstant(), argc, target,
                        jsgraph()->UndefinedConstant(), entry, argc, context,
                        frame_state, efalse1, if_false1);
                }

                if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
                efalse0 = graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
                vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    vtrue1, vfalse1, if_false0);
            }

            control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
            effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
            Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                vtrue0, vfalse0, control);

            // Convert the hole to undefined. Do this last, so that we can optimize
            // conversion operator via some smart strength reduction in many cases.
            if (IsHoleyElementsKind(kind)) {
                value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
            }

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES6 section 22.1.3.23 Array.prototype.slice ( )
        Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node)
        {
            if (!FLAG_turbo_inline_array_builtins)
                return NoChange();
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* start = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->ZeroConstant();
            Node* end = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Optimize for the case where we simply clone the {receiver},
            // i.e. when the {start} is zero and the {end} is undefined
            // (meaning it will be set to {receiver}s "length" property).
            if (!NumberMatcher(start).Is(0) || !HeapObjectMatcher(end).Is(factory()->undefined_value())) {
                return NoChange();
            }

            // Try to determine the {receiver} maps.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            // Check that the maps are of JSArray (and more).
            // TODO(turbofan): Consider adding special case for the common pattern
            // `slice.call(arguments)`, for example jQuery makes heavy use of that.
            bool can_be_holey = false;
            for (Handle<Map> map : receiver_maps) {
                MapRef receiver_map(broker(), map);
                if (!receiver_map.supports_fast_array_iteration())
                    return NoChange();

                if (IsHoleyElementsKind(receiver_map.elements_kind())) {
                    can_be_holey = true;
                }
            }

            if (!dependencies()->DependOnArraySpeciesProtector())
                return NoChange();
            if (can_be_holey) {
                if (!dependencies()->DependOnNoElementsProtector())
                    UNREACHABLE();
            }

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            // TODO(turbofan): We can do even better here, either adding a CloneArray
            // simplified operator, whose output type indicates that it's an Array,
            // saving subsequent checks, or yet better, by introducing new operators
            // CopySmiOrObjectElements / CopyDoubleElements and inlining the JSArray
            // allocation in here. That way we'd even get escape analysis and scalar
            // replacement to help in some cases.
            Callable callable = Builtins::CallableFor(isolate(), Builtins::kCloneFastJSArray);
            auto call_descriptor = Linkage::GetStubCallDescriptor(
                graph()->zone(), callable.descriptor(),
                callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
                Operator::kNoThrow | Operator::kNoDeopt);

            // Calls to Builtins::kCloneFastJSArray produce COW arrays
            // if the original array is COW
            Node* clone = effect = graph()->NewNode(
                common()->Call(call_descriptor), jsgraph()->HeapConstant(callable.code()),
                receiver, context, effect, control);

            ReplaceWithValue(node, clone, effect, control);
            return Replace(clone);
        }

        // ES6 section 22.1.2.2 Array.isArray ( arg )
        Reduction JSCallReducer::ReduceArrayIsArray(Node* node)
        {
            // We certainly know that undefined is not an array.
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* object = NodeProperties::GetValueInput(node, 2);
            node->ReplaceInput(0, object);
            node->ReplaceInput(1, context);
            node->ReplaceInput(2, frame_state);
            node->ReplaceInput(3, effect);
            node->ReplaceInput(4, control);
            node->TrimInputCount(5);
            NodeProperties::ChangeOp(node, javascript()->ObjectIsArray());
            return Changed(node);
        }

        Reduction JSCallReducer::ReduceArrayIterator(Node* node, IterationKind kind)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Check if we know that {receiver} is a valid JSReceiver.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());
            for (Handle<Map> map : receiver_maps) {
                MapRef receiver_map(broker(), map);
                if (!receiver_map.IsJSReceiverMap())
                    return NoChange();
            }

            // Morph the {node} into a JSCreateArrayIterator with the given {kind}.
            RelaxControls(node);
            node->ReplaceInput(0, receiver);
            node->ReplaceInput(1, context);
            node->ReplaceInput(2, effect);
            node->ReplaceInput(3, control);
            node->TrimInputCount(4);
            NodeProperties::ChangeOp(node, javascript()->CreateArrayIterator(kind));
            return Changed(node);
        }

        namespace {

            bool InferIteratedObjectMaps(JSHeapBroker* broker, Node* iterator,
                ZoneHandleSet<Map>* iterated_object_maps)
            {
                DCHECK_EQ(IrOpcode::kJSCreateArrayIterator, iterator->opcode());
                Node* iterated_object = NodeProperties::GetValueInput(iterator, 0);
                Node* effect = NodeProperties::GetEffectInput(iterator);

                NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker, iterated_object, effect,
                    iterated_object_maps);
                return result != NodeProperties::kNoReceiverMaps;
            }

        } // namespace

        // ES #sec-%arrayiteratorprototype%.next
        Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            Node* iterator = NodeProperties::GetValueInput(node, 1);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            // Check if the {iterator} is a JSCreateArrayIterator.
            if (iterator->opcode() != IrOpcode::kJSCreateArrayIterator)
                return NoChange();
            IterationKind const iteration_kind = CreateArrayIteratorParametersOf(iterator->op()).kind();

            // Try to infer the [[IteratedObject]] maps from the {iterator}.
            ZoneHandleSet<Map> iterated_object_maps;
            if (!InferIteratedObjectMaps(broker(), iterator, &iterated_object_maps)) {
                return NoChange();
            }
            DCHECK_NE(0, iterated_object_maps.size());

            // Check that various {iterated_object_maps} have compatible elements kinds.
            ElementsKind elements_kind = MapRef(broker(), iterated_object_maps[0]).elements_kind();
            if (IsFixedTypedArrayElementsKind(elements_kind)) {
                // TurboFan doesn't support loading from BigInt typed arrays yet.
                if (elements_kind == BIGUINT64_ELEMENTS || elements_kind == BIGINT64_ELEMENTS) {
                    return NoChange();
                }
                for (Handle<Map> map : iterated_object_maps) {
                    MapRef iterated_object_map(broker(), map);
                    if (iterated_object_map.elements_kind() != elements_kind) {
                        return NoChange();
                    }
                }
            } else {
                if (!CanInlineArrayIteratingBuiltin(broker(), iterated_object_maps,
                        &elements_kind)) {
                    return NoChange();
                }
            }

            // Install code dependency on the array protector for holey arrays.
            if (IsHoleyElementsKind(elements_kind)) {
                if (!dependencies()->DependOnNoElementsProtector())
                    UNREACHABLE();
            }

            // Load the (current) {iterated_object} from the {iterator}.
            Node* iterated_object = effect = graph()->NewNode(simplified()->LoadField(
                                                                  AccessBuilder::ForJSArrayIteratorIteratedObject()),
                iterator, effect, control);

            // Ensure that the {iterated_object} map didn't change.
            effect = graph()->NewNode(
                simplified()->CheckMaps(CheckMapsFlag::kNone, iterated_object_maps,
                    p.feedback()),
                iterated_object, effect, control);

            if (IsFixedTypedArrayElementsKind(elements_kind)) {
                // See if we can skip the detaching check.
                if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
                    // Bail out if the {iterated_object}s JSArrayBuffer was detached.
                    Node* buffer = effect = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
                        iterated_object, effect, control);
                    Node* buffer_bit_field = effect = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()),
                        buffer, effect, control);
                    Node* check = graph()->NewNode(
                        simplified()->NumberEqual(),
                        graph()->NewNode(
                            simplified()->NumberBitwiseAnd(), buffer_bit_field,
                            jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)),
                        jsgraph()->ZeroConstant());
                    effect = graph()->NewNode(
                        simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached,
                            p.feedback()),
                        check, effect, control);
                }
            }

            // Load the [[NextIndex]] from the {iterator} and leverage the fact
            // that we definitely know that it's in Unsigned32 range since the
            // {iterated_object} is either a JSArray or a JSTypedArray. For the
            // latter case we even know that it's a Smi in UnsignedSmall range.
            FieldAccess index_access = AccessBuilder::ForJSArrayIteratorNextIndex();
            if (IsFixedTypedArrayElementsKind(elements_kind)) {
                index_access.type = TypeCache::Get()->kJSTypedArrayLengthType;
                index_access.machine_type = MachineType::TaggedSigned();
                index_access.write_barrier_kind = kNoWriteBarrier;
            } else {
                index_access.type = TypeCache::Get()->kJSArrayLengthType;
            }
            Node* index = effect = graph()->NewNode(simplified()->LoadField(index_access),
                iterator, effect, control);

            // Load the elements of the {iterated_object}. While it feels
            // counter-intuitive to place the elements pointer load before
            // the condition below, as it might not be needed (if the {index}
            // is out of bounds for the {iterated_object}), it's better this
            // way as it allows the LoadElimination to eliminate redundant
            // reloads of the elements pointer.
            Node* elements = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
                iterated_object, effect, control);

            // Load the length of the {iterated_object}. Due to the map checks we
            // already know something about the length here, which we can leverage
            // to generate Word32 operations below without additional checking.
            FieldAccess length_access = IsFixedTypedArrayElementsKind(elements_kind)
                ? AccessBuilder::ForJSTypedArrayLength()
                : AccessBuilder::ForJSArrayLength(elements_kind);
            Node* length = effect = graph()->NewNode(
                simplified()->LoadField(length_access), iterated_object, effect, control);

            // Check whether {index} is within the valid range for the {iterated_object}.
            Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length);
            Node* branch = graph()->NewNode(common()->Branch(BranchHint::kNone), check, control);

            Node* done_true;
            Node* value_true;
            Node* etrue = effect;
            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            {
                // We know that the {index} is range of the {length} now.
                index = etrue = graph()->NewNode(
                    common()->TypeGuard(
                        Type::Range(0.0, length_access.type.Max() - 1.0, graph()->zone())),
                    index, etrue, if_true);

                done_true = jsgraph()->FalseConstant();
                if (iteration_kind == IterationKind::kKeys) {
                    // Just return the {index}.
                    value_true = index;
                } else {
                    DCHECK(iteration_kind == IterationKind::kEntries || iteration_kind == IterationKind::kValues);

                    if (IsFixedTypedArrayElementsKind(elements_kind)) {
                        Node* base_ptr = etrue = graph()->NewNode(
                            simplified()->LoadField(
                                AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
                            elements, etrue, if_true);
                        Node* external_ptr = etrue = graph()->NewNode(
                            simplified()->LoadField(
                                AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
                            elements, etrue, if_true);

                        ExternalArrayType array_type = kExternalInt8Array;
                        switch (elements_kind) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
    case TYPE##_ELEMENTS:                         \
        array_type = kExternal##Type##Array;      \
        break;
                            TYPED_ARRAYS(TYPED_ARRAY_CASE)
                        default:
                            UNREACHABLE();
#undef TYPED_ARRAY_CASE
                        }

                        Node* buffer = etrue = graph()->NewNode(simplified()->LoadField(
                                                                    AccessBuilder::ForJSArrayBufferViewBuffer()),
                            iterated_object, etrue, if_true);

                        value_true = etrue = graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer,
                            base_ptr, external_ptr, index, etrue, if_true);
                    } else {
                        value_true = etrue = graph()->NewNode(
                            simplified()->LoadElement(
                                AccessBuilder::ForFixedArrayElement(elements_kind)),
                            elements, index, etrue, if_true);

                        // Convert hole to undefined if needed.
                        if (elements_kind == HOLEY_ELEMENTS || elements_kind == HOLEY_SMI_ELEMENTS) {
                            value_true = graph()->NewNode(
                                simplified()->ConvertTaggedHoleToUndefined(), value_true);
                        } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
                            // TODO(6587): avoid deopt if not all uses of value are truncated.
                            CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
                            value_true = etrue = graph()->NewNode(
                                simplified()->CheckFloat64Hole(mode, p.feedback()), value_true,
                                etrue, if_true);
                        }
                    }

                    if (iteration_kind == IterationKind::kEntries) {
                        // Allocate elements for key/value pair
                        value_true = etrue = graph()->NewNode(javascript()->CreateKeyValueArray(), index,
                            value_true, context, etrue);
                    } else {
                        DCHECK_EQ(IterationKind::kValues, iteration_kind);
                    }
                }

                // Increment the [[NextIndex]] field in the {iterator}. The TypeGuards
                // above guarantee that the {next_index} is in the UnsignedSmall range.
                Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
                    jsgraph()->OneConstant());
                etrue = graph()->NewNode(simplified()->StoreField(index_access), iterator,
                    next_index, etrue, if_true);
            }

            Node* done_false;
            Node* value_false;
            Node* efalse = effect;
            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            {
                // iterator.[[NextIndex]] >= array.length, stop iterating.
                done_false = jsgraph()->TrueConstant();
                value_false = jsgraph()->UndefinedConstant();

                if (!IsFixedTypedArrayElementsKind(elements_kind)) {
                    // Mark the {iterator} as exhausted by setting the [[NextIndex]] to a
                    // value that will never pass the length check again (aka the maximum
                    // value possible for the specific iterated object). Note that this is
                    // different from what the specification says, which is changing the
                    // [[IteratedObject]] field to undefined, but that makes it difficult
                    // to eliminate the map checks and "length" accesses in for..of loops.
                    //
                    // This is not necessary for JSTypedArray's, since the length of those
                    // cannot change later and so if we were ever out of bounds for them
                    // we will stay out-of-bounds forever.
                    Node* end_index = jsgraph()->Constant(index_access.type.Max());
                    efalse = graph()->NewNode(simplified()->StoreField(index_access),
                        iterator, end_index, efalse, if_false);
                }
            }

            control = graph()->NewNode(common()->Merge(2), if_true, if_false);
            effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
            Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                value_true, value_false, control);
            Node* done = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                done_true, done_false, control);

            // Create IteratorResult object.
            value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
                value, done, context, effect);
            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
        // ES6 section 21.1.3.3 String.prototype.codePointAt ( pos )
        Reduction JSCallReducer::ReduceStringPrototypeStringAt(
            const Operator* string_access_operator, Node* node)
        {
            DCHECK(string_access_operator->opcode() == IrOpcode::kStringCharCodeAt || string_access_operator->opcode() == IrOpcode::kStringCodePointAt);
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* index = node->op()->ValueInputCount() >= 3
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->ZeroConstant();
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Ensure that the {receiver} is actually a String.
            receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                receiver, effect, control);

            // Determine the {receiver} length.
            Node* receiver_length = graph()->NewNode(simplified()->StringLength(), receiver);

            // Check that the {index} is within range.
            index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
                index, receiver_length, effect, control);

            // Return the character from the {receiver} as single character string.
            Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
            Node* value = effect = graph()->NewNode(string_access_operator, receiver,
                masked_index, effect, control);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES section 21.1.3.1 String.prototype.charAt ( pos )
        Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* index = node->op()->ValueInputCount() >= 3
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->ZeroConstant();
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Ensure that the {receiver} is actually a String.
            receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                receiver, effect, control);

            // Determine the {receiver} length.
            Node* receiver_length = graph()->NewNode(simplified()->StringLength(), receiver);

            // Check that the {index} is within range.
            index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
                index, receiver_length, effect, control);

            // Return the character from the {receiver} as single character string.
            Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
            Node* value = effect = graph()->NewNode(simplified()->StringCharCodeAt(), receiver, masked_index,
                effect, control);
            value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

#ifdef V8_INTL_SUPPORT

        Reduction JSCallReducer::ReduceStringPrototypeToLowerCaseIntl(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            Node* receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                NodeProperties::GetValueInput(node, 1), effect, control);

            NodeProperties::ReplaceEffectInput(node, effect);
            RelaxEffectsAndControls(node);
            node->ReplaceInput(0, receiver);
            node->TrimInputCount(1);
            NodeProperties::ChangeOp(node, simplified()->StringToLowerCaseIntl());
            NodeProperties::SetType(node, Type::String());
            return Changed(node);
        }

        Reduction JSCallReducer::ReduceStringPrototypeToUpperCaseIntl(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            Node* receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                NodeProperties::GetValueInput(node, 1), effect, control);

            NodeProperties::ReplaceEffectInput(node, effect);
            RelaxEffectsAndControls(node);
            node->ReplaceInput(0, receiver);
            node->TrimInputCount(1);
            NodeProperties::ChangeOp(node, simplified()->StringToUpperCaseIntl());
            NodeProperties::SetType(node, Type::String());
            return Changed(node);
        }

#endif // V8_INTL_SUPPORT

        // ES #sec-string.fromcharcode
        Reduction JSCallReducer::ReduceStringFromCharCode(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() == 3) {
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);
                Node* input = NodeProperties::GetValueInput(node, 2);

                input = effect = graph()->NewNode(
                    simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
                        p.feedback()),
                    input, effect, control);

                Node* value = graph()->NewNode(simplified()->StringFromSingleCharCode(), input);
                ReplaceWithValue(node, value, effect);
                return Replace(value);
            }
            return NoChange();
        }

        // ES #sec-string.fromcodepoint
        Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() == 3) {
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);
                Node* input = NodeProperties::GetValueInput(node, 2);

                input = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), input,
                    jsgraph()->Constant(0x10FFFF + 1), effect, control);

                Node* value = graph()->NewNode(
                    simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF32), input);
                ReplaceWithValue(node, value, effect);
                return Replace(value);
            }
            return NoChange();
        }

        Reduction JSCallReducer::ReduceStringPrototypeIterator(Node* node)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                NodeProperties::GetValueInput(node, 1), effect, control);
            Node* iterator = effect = graph()->NewNode(javascript()->CreateStringIterator(), receiver,
                jsgraph()->NoContextConstant(), effect);
            ReplaceWithValue(node, iterator, effect, control);
            return Replace(iterator);
        }

        Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node)
        {
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect,
                    JS_STRING_ITERATOR_TYPE)) {
                Node* string = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()),
                    receiver, effect, control);
                Node* index = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()),
                    receiver, effect, control);
                Node* length = graph()->NewNode(simplified()->StringLength(), string);

                // branch0: if (index < length)
                Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index, length);
                Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kNone), check0, control);

                Node* etrue0 = effect;
                Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
                Node* done_true;
                Node* vtrue0;
                {
                    done_true = jsgraph()->FalseConstant();
                    Node* codepoint = etrue0 = graph()->NewNode(
                        simplified()->StringCodePointAt(UnicodeEncoding::UTF16), string,
                        index, etrue0, if_true0);
                    vtrue0 = graph()->NewNode(
                        simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF16),
                        codepoint);

                    // Update iterator.[[NextIndex]]
                    Node* char_length = graph()->NewNode(simplified()->StringLength(), vtrue0);
                    index = graph()->NewNode(simplified()->NumberAdd(), index, char_length);
                    etrue0 = graph()->NewNode(
                        simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()),
                        receiver, index, etrue0, if_true0);
                }

                Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
                Node* done_false;
                Node* vfalse0;
                {
                    vfalse0 = jsgraph()->UndefinedConstant();
                    done_false = jsgraph()->TrueConstant();
                }

                control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
                effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control);
                Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    vtrue0, vfalse0, control);
                Node* done = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    done_true, done_false, control);

                value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
                    value, done, context, effect);

                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            }
            return NoChange();
        }

        // ES #sec-string.prototype.concat
        Reduction JSCallReducer::ReduceStringPrototypeConcat(Node* node)
        {
            if (node->op()->ValueInputCount() < 2 || node->op()->ValueInputCount() > 3) {
                return NoChange();
            }
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                NodeProperties::GetValueInput(node, 1), effect, control);

            if (node->op()->ValueInputCount() < 3) {
                ReplaceWithValue(node, receiver, effect, control);
                return Replace(receiver);
            }

            Node* argument = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
                NodeProperties::GetValueInput(node, 2), effect, control);
            Node* receiver_length = graph()->NewNode(simplified()->StringLength(), receiver);
            Node* argument_length = graph()->NewNode(simplified()->StringLength(), argument);
            Node* length = graph()->NewNode(simplified()->NumberAdd(), receiver_length,
                argument_length);
            length = effect = graph()->NewNode(
                simplified()->CheckBounds(p.feedback()), length,
                jsgraph()->Constant(String::kMaxLength + 1), effect, control);

            Node* value = graph()->NewNode(simplified()->StringConcat(), length, receiver,
                argument);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        Node* JSCallReducer::CreateArtificialFrameState(
            Node* node, Node* outer_frame_state, int parameter_count,
            BailoutId bailout_id, FrameStateType frame_state_type,
            const SharedFunctionInfoRef& shared, Node* context)
        {
            const FrameStateFunctionInfo* state_info = common()->CreateFrameStateFunctionInfo(
                frame_state_type, parameter_count + 1, 0, shared.object());

            const Operator* op = common()->FrameState(
                bailout_id, OutputFrameStateCombine::Ignore(), state_info);
            const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
            Node* node0 = graph()->NewNode(op0);
            std::vector<Node*> params;
            params.reserve(parameter_count + 1);
            for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
                params.push_back(node->InputAt(1 + parameter));
            }
            const Operator* op_param = common()->StateValues(
                static_cast<int>(params.size()), SparseInputMask::Dense());
            Node* params_node = graph()->NewNode(
                op_param, static_cast<int>(params.size()), &params.front());
            if (!context) {
                context = jsgraph()->UndefinedConstant();
            }
            return graph()->NewNode(op, params_node, node0, node0, context,
                node->InputAt(0), outer_frame_state);
        }

        Reduction JSCallReducer::ReducePromiseConstructor(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
            ConstructParameters const& p = ConstructParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            // We only inline when we have the executor.
            if (arity < 1)
                return NoChange();
            Node* target = NodeProperties::GetValueInput(node, 0);
            Node* executor = NodeProperties::GetValueInput(node, 1);
            Node* new_target = NodeProperties::GetValueInput(node, arity + 1);

            Node* context = NodeProperties::GetContextInput(node);
            Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            if (!FLAG_experimental_inline_promise_constructor)
                return NoChange();

            // Only handle builtins Promises, not subclasses.
            if (target != new_target)
                return NoChange();

            if (!dependencies()->DependOnPromiseHookProtector())
                return NoChange();

            SharedFunctionInfoRef promise_shared = native_context().promise_function().shared();

            // Insert a construct stub frame into the chain of frame states. This will
            // reconstruct the proper frame when deoptimizing within the constructor.
            // For the frame state, we only provide the executor parameter, even if more
            // arugments were passed. This is not observable from JS.
            DCHECK_EQ(1, promise_shared.internal_formal_parameter_count());
            Node* constructor_frame_state = CreateArtificialFrameState(
                node, outer_frame_state, 1, BailoutId::ConstructStubInvoke(),
                FrameStateType::kConstructStub, promise_shared, context);

            // The deopt continuation of this frame state is never called; the frame state
            // is only necessary to obtain the right stack trace.
            const std::vector<Node*> checkpoint_parameters({
                jsgraph()->UndefinedConstant(), /* receiver */
                jsgraph()->UndefinedConstant(), /* promise */
                jsgraph()->UndefinedConstant(), /* reject function */
                jsgraph()->TheHoleConstant() /* exception */
            });
            int checkpoint_parameters_size = static_cast<int>(checkpoint_parameters.size());
            Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), promise_shared,
                Builtins::kPromiseConstructorLazyDeoptContinuation, target, context,
                checkpoint_parameters.data(), checkpoint_parameters_size,
                constructor_frame_state, ContinuationFrameStateMode::LAZY);

            // Check if executor is callable
            Node* check_fail = nullptr;
            Node* check_throw = nullptr;
            WireInCallbackIsCallableCheck(executor, context, frame_state, effect,
                &control, &check_fail, &check_throw);

            // Create the resulting JSPromise.
            Node* promise = effect = graph()->NewNode(javascript()->CreatePromise(), context, effect);

            // 8. CreatePromiseResolvingFunctions
            // Allocate a promise context for the closures below.
            Node* promise_context = effect = graph()->NewNode(
                javascript()->CreateFunctionContext(
                    handle(native_context().object()->scope_info(), isolate()),
                    PromiseBuiltins::kPromiseContextLength - Context::MIN_CONTEXT_SLOTS,
                    FUNCTION_SCOPE),
                context, effect, control);
            effect = graph()->NewNode(
                simplified()->StoreField(
                    AccessBuilder::ForContextSlot(PromiseBuiltins::kPromiseSlot)),
                promise_context, promise, effect, control);
            effect = graph()->NewNode(
                simplified()->StoreField(
                    AccessBuilder::ForContextSlot(PromiseBuiltins::kAlreadyResolvedSlot)),
                promise_context, jsgraph()->FalseConstant(), effect, control);
            effect = graph()->NewNode(
                simplified()->StoreField(
                    AccessBuilder::ForContextSlot(PromiseBuiltins::kDebugEventSlot)),
                promise_context, jsgraph()->TrueConstant(), effect, control);

            // Allocate the closure for the resolve case.
            SharedFunctionInfoRef resolve_shared = native_context().promise_capability_default_resolve_shared_fun();
            Node* resolve = effect = graph()->NewNode(
                javascript()->CreateClosure(
                    resolve_shared.object(), factory()->many_closures_cell(),
                    handle(resolve_shared.object()->GetCode(), isolate())),
                promise_context, effect, control);

            // Allocate the closure for the reject case.
            SharedFunctionInfoRef reject_shared = native_context().promise_capability_default_reject_shared_fun();
            Node* reject = effect = graph()->NewNode(
                javascript()->CreateClosure(
                    reject_shared.object(), factory()->many_closures_cell(),
                    handle(reject_shared.object()->GetCode(), isolate())),
                promise_context, effect, control);

            const std::vector<Node*> checkpoint_parameters_continuation(
                { jsgraph()->UndefinedConstant() /* receiver */, promise, reject });
            int checkpoint_parameters_continuation_size = static_cast<int>(checkpoint_parameters_continuation.size());
            // This continuation just returns the created promise and takes care of
            // exceptions thrown by the executor.
            frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), promise_shared,
                Builtins::kPromiseConstructorLazyDeoptContinuation, target, context,
                checkpoint_parameters_continuation.data(),
                checkpoint_parameters_continuation_size, constructor_frame_state,
                ContinuationFrameStateMode::LAZY_WITH_CATCH);

            // 9. Call executor with both resolving functions
            effect = control = graph()->NewNode(
                javascript()->Call(4, p.frequency(), VectorSlotPair(),
                    ConvertReceiverMode::kNullOrUndefined,
                    SpeculationMode::kDisallowSpeculation),
                executor, jsgraph()->UndefinedConstant(), resolve, reject, context,
                frame_state, effect, control);

            Node* exception_effect = effect;
            Node* exception_control = control;
            {
                Node* reason = exception_effect = exception_control = graph()->NewNode(
                    common()->IfException(), exception_control, exception_effect);
                // 10a. Call reject if the call to executor threw.
                exception_effect = exception_control = graph()->NewNode(
                    javascript()->Call(3, p.frequency(), VectorSlotPair(),
                        ConvertReceiverMode::kNullOrUndefined,
                        SpeculationMode::kDisallowSpeculation),
                    reject, jsgraph()->UndefinedConstant(), reason, context, frame_state,
                    exception_effect, exception_control);

                // Rewire potential exception edges.
                Node* on_exception = nullptr;
                if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                    RewirePostCallbackExceptionEdges(check_throw, on_exception,
                        exception_effect, &check_fail,
                        &exception_control);
                }
            }

            Node* success_effect = effect;
            Node* success_control = control;
            {
                success_control = graph()->NewNode(common()->IfSuccess(), success_control);
            }

            control = graph()->NewNode(common()->Merge(2), success_control, exception_control);
            effect = graph()->NewNode(common()->EffectPhi(2), success_effect,
                exception_effect, control);

            // Wire up the branch for the case when IsCallable fails for the executor.
            // Since {check_throw} is an unconditional throw, it's impossible to
            // return a successful completion. Therefore, we simply connect the successful
            // completion to the graph end.
            Node* throw_node = graph()->NewNode(common()->Throw(), check_throw, check_fail);
            NodeProperties::MergeControlToEnd(graph(), common(), throw_node);

            ReplaceWithValue(node, promise, effect, control);
            return Replace(promise);
        }

        // V8 Extras: v8.createPromise(parent)
        Reduction JSCallReducer::ReducePromiseInternalConstructor(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);

            // Check that promises aren't being observed through (debug) hooks.
            if (!dependencies()->DependOnPromiseHookProtector())
                return NoChange();

            // Create a new pending promise.
            Node* value = effect = graph()->NewNode(javascript()->CreatePromise(), context, effect);

            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        // V8 Extras: v8.rejectPromise(promise, reason)
        Reduction JSCallReducer::ReducePromiseInternalReject(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* promise = node->op()->ValueInputCount() >= 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* reason = node->op()->ValueInputCount() >= 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            Node* debug_event = jsgraph()->TrueConstant();
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Reject the {promise} using the given {reason}, and trigger debug logic.
            Node* value = effect = graph()->NewNode(javascript()->RejectPromise(), promise, reason,
                debug_event, context, frame_state, effect, control);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // V8 Extras: v8.resolvePromise(promise, resolution)
        Reduction JSCallReducer::ReducePromiseInternalResolve(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* promise = node->op()->ValueInputCount() >= 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* resolution = node->op()->ValueInputCount() >= 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Resolve the {promise} using the given {resolution}.
            Node* value = effect = graph()->NewNode(javascript()->ResolvePromise(), promise, resolution,
                context, frame_state, effect, control);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES section #sec-promise.prototype.catch
        Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            int arity = static_cast<int>(p.arity() - 2);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Check if we know something about {receiver} already.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());

            // Check whether all {receiver_maps} are JSPromise maps and
            // have the initial Promise.prototype as their [[Prototype]].
            for (Handle<Map> map : receiver_maps) {
                MapRef receiver_map(broker(), map);
                if (!receiver_map.IsJSPromiseMap())
                    return NoChange();
                receiver_map.SerializePrototype();
                if (!receiver_map.prototype().equals(
                        native_context().promise_prototype())) {
                    return NoChange();
                }
            }

            // Check that the Promise.then protector is intact. This protector guards
            // that all JSPromise instances whose [[Prototype]] is the initial
            // %PromisePrototype% yield the initial %PromisePrototype%.then method
            // when looking up "then".
            if (!dependencies()->DependOnPromiseThenProtector())
                return NoChange();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            // Massage the {node} to call "then" instead by first removing all inputs
            // following the onRejected parameter, and then filling up the parameters
            // to two inputs from the left with undefined.
            Node* target = jsgraph()->Constant(native_context().promise_then());
            NodeProperties::ReplaceValueInput(node, target, 0);
            NodeProperties::ReplaceEffectInput(node, effect);
            for (; arity > 1; --arity)
                node->RemoveInput(3);
            for (; arity < 2; ++arity) {
                node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant());
            }
            NodeProperties::ChangeOp(
                node, javascript()->Call(2 + arity, p.frequency(), p.feedback(), ConvertReceiverMode::kNotNullOrUndefined, p.speculation_mode()));
            Reduction const reduction = ReducePromisePrototypeThen(node);
            return reduction.Changed() ? reduction : Changed(node);
        }

        // ES section #sec-promise.prototype.finally
        Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* on_finally = arity >= 1 ? NodeProperties::GetValueInput(node, 2)
                                          : jsgraph()->UndefinedConstant();
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            // Check if we know something about {receiver} already.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());

            // Check whether all {receiver_maps} are JSPromise maps and
            // have the initial Promise.prototype as their [[Prototype]].
            for (Handle<Map> map : receiver_maps) {
                MapRef receiver_map(broker(), map);
                if (!receiver_map.IsJSPromiseMap())
                    return NoChange();
                receiver_map.SerializePrototype();
                if (!receiver_map.prototype().equals(
                        native_context().promise_prototype())) {
                    return NoChange();
                }
            }

            // Check that promises aren't being observed through (debug) hooks.
            if (!dependencies()->DependOnPromiseHookProtector())
                return NoChange();

            // Check that the Promise#then protector is intact. This protector guards
            // that all JSPromise instances whose [[Prototype]] is the initial
            // %PromisePrototype% yield the initial %PromisePrototype%.then method
            // when looking up "then".
            if (!dependencies()->DependOnPromiseThenProtector())
                return NoChange();

            // Also check that the @@species protector is intact, which guards the
            // lookup of "constructor" on JSPromise instances, whoch [[Prototype]] is
            // the initial %PromisePrototype%, and the Symbol.species lookup on the
            // %PromisePrototype%.
            if (!dependencies()->DependOnPromiseSpeciesProtector())
                return NoChange();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            // Check if {on_finally} is callable, and if so wrap it into appropriate
            // closures that perform the finalization.
            Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), on_finally);
            Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            Node* etrue = effect;
            Node* catch_true;
            Node* then_true;
            {
                Node* context = jsgraph()->Constant(native_context());
                Node* constructor = jsgraph()->Constant(native_context().promise_function());

                // Allocate shared context for the closures below.
                context = etrue = graph()->NewNode(
                    javascript()->CreateFunctionContext(
                        handle(native_context().object()->scope_info(), isolate()),
                        PromiseBuiltins::kPromiseFinallyContextLength - Context::MIN_CONTEXT_SLOTS,
                        FUNCTION_SCOPE),
                    context, etrue, if_true);
                etrue = graph()->NewNode(
                    simplified()->StoreField(
                        AccessBuilder::ForContextSlot(PromiseBuiltins::kOnFinallySlot)),
                    context, on_finally, etrue, if_true);
                etrue = graph()->NewNode(
                    simplified()->StoreField(
                        AccessBuilder::ForContextSlot(PromiseBuiltins::kConstructorSlot)),
                    context, constructor, etrue, if_true);

                // Allocate the closure for the reject case.
                SharedFunctionInfoRef catch_finally = native_context().promise_catch_finally_shared_fun();
                catch_true = etrue = graph()->NewNode(
                    javascript()->CreateClosure(
                        catch_finally.object(), factory()->many_closures_cell(),
                        handle(catch_finally.object()->GetCode(), isolate())),
                    context, etrue, if_true);

                // Allocate the closure for the fulfill case.
                SharedFunctionInfoRef then_finally = native_context().promise_then_finally_shared_fun();
                then_true = etrue = graph()->NewNode(
                    javascript()->CreateClosure(
                        then_finally.object(), factory()->many_closures_cell(),
                        handle(then_finally.object()->GetCode(), isolate())),
                    context, etrue, if_true);
            }

            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            Node* efalse = effect;
            Node* catch_false = on_finally;
            Node* then_false = on_finally;

            control = graph()->NewNode(common()->Merge(2), if_true, if_false);
            effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
            Node* catch_finally = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                catch_true, catch_false, control);
            Node* then_finally = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                then_true, then_false, control);

            // At this point we definitely know that {receiver} has one of the
            // {receiver_maps}, so insert a MapGuard as a hint for the lowering
            // of the call to "then" below.
            effect = graph()->NewNode(simplified()->MapGuard(receiver_maps), receiver,
                effect, control);

            // Massage the {node} to call "then" instead by first removing all inputs
            // following the onFinally parameter, and then replacing the only parameter
            // input with the {on_finally} value.
            Node* target = jsgraph()->Constant(native_context().promise_then());
            NodeProperties::ReplaceValueInput(node, target, 0);
            NodeProperties::ReplaceEffectInput(node, effect);
            NodeProperties::ReplaceControlInput(node, control);
            for (; arity > 2; --arity)
                node->RemoveInput(2);
            for (; arity < 2; ++arity)
                node->InsertInput(graph()->zone(), 2, then_finally);
            node->ReplaceInput(2, then_finally);
            node->ReplaceInput(3, catch_finally);
            NodeProperties::ChangeOp(
                node, javascript()->Call(2 + arity, p.frequency(), p.feedback(), ConvertReceiverMode::kNotNullOrUndefined, p.speculation_mode()));
            Reduction const reduction = ReducePromisePrototypeThen(node);
            return reduction.Changed() ? reduction : Changed(node);
        }

        Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* on_fulfilled = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* on_rejected = node->op()->ValueInputCount() > 3
                ? NodeProperties::GetValueInput(node, 3)
                : jsgraph()->UndefinedConstant();
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);

            // Check if we know something about {receiver} already.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());

            // Check whether all {receiver_maps} are JSPromise maps and
            // have the initial Promise.prototype as their [[Prototype]].
            for (Handle<Map> map : receiver_maps) {
                MapRef receiver_map(broker(), map);
                if (!receiver_map.IsJSPromiseMap())
                    return NoChange();
                receiver_map.SerializePrototype();
                if (!receiver_map.prototype().equals(
                        native_context().promise_prototype())) {
                    return NoChange();
                }
            }

            // Check that promises aren't being observed through (debug) hooks.
            if (!dependencies()->DependOnPromiseHookProtector())
                return NoChange();

            // Check if the @@species protector is intact. The @@species protector
            // guards the "constructor" lookup on all JSPromise instances and the
            // initial Promise.prototype, as well as the  Symbol.species lookup on
            // the Promise constructor.
            if (!dependencies()->DependOnPromiseSpeciesProtector())
                return NoChange();

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, receiver_maps, p.feedback(), receiver, effect, control);

            // Check that {on_fulfilled} is callable.
            on_fulfilled = graph()->NewNode(
                common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
                graph()->NewNode(simplified()->ObjectIsCallable(), on_fulfilled),
                on_fulfilled, jsgraph()->UndefinedConstant());

            // Check that {on_rejected} is callable.
            on_rejected = graph()->NewNode(
                common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
                graph()->NewNode(simplified()->ObjectIsCallable(), on_rejected),
                on_rejected, jsgraph()->UndefinedConstant());

            // Create the resulting JSPromise.
            Node* promise = effect = graph()->NewNode(javascript()->CreatePromise(), context, effect);

            // Chain {result} onto {receiver}.
            promise = effect = graph()->NewNode(
                javascript()->PerformPromiseThen(), receiver, on_fulfilled, on_rejected,
                promise, context, frame_state, effect, control);

            // At this point we know that {promise} is going to have the
            // initial Promise map, since even if {PerformPromiseThen}
            // above called into the host rejection tracker, the {promise}
            // doesn't escape to user JavaScript. So bake this information
            // into the graph such that subsequent passes can use the
            // information for further optimizations.
            MapRef promise_map = native_context().promise_function().initial_map();
            effect = graph()->NewNode(
                simplified()->MapGuard(ZoneHandleSet<Map>(promise_map.object())), promise,
                effect, control);

            ReplaceWithValue(node, promise, effect, control);
            return Replace(promise);
        }

        // ES section #sec-promise.resolve
        Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* value = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Check if we know something about {receiver} already.
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult infer_receiver_maps_result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) {
                return NoChange();
            }
            DCHECK_NE(0, receiver_maps.size());

            // Only reduce when all {receiver_maps} are JSReceiver maps.
            for (Handle<Map> map : receiver_maps) {
                MapRef receiver_map(broker(), map);
                if (!receiver_map.IsJSReceiverMap())
                    return NoChange();
            }

            // Morph the {node} into a JSPromiseResolve operation.
            node->ReplaceInput(0, receiver);
            node->ReplaceInput(1, value);
            node->ReplaceInput(2, context);
            node->ReplaceInput(3, frame_state);
            node->ReplaceInput(4, effect);
            node->ReplaceInput(5, control);
            node->TrimInputCount(6);
            NodeProperties::ChangeOp(node, javascript()->PromiseResolve());
            return Changed(node);
        }

        // ES #sec-typedarray-constructors
        Reduction JSCallReducer::ReduceTypedArrayConstructor(
            Node* node, const SharedFunctionInfoRef& shared)
        {
            DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
            ConstructParameters const& p = ConstructParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            Node* target = NodeProperties::GetValueInput(node, 0);
            Node* arg1 = (arity >= 1) ? NodeProperties::GetValueInput(node, 1)
                                      : jsgraph()->UndefinedConstant();
            Node* arg2 = (arity >= 2) ? NodeProperties::GetValueInput(node, 2)
                                      : jsgraph()->UndefinedConstant();
            Node* arg3 = (arity >= 3) ? NodeProperties::GetValueInput(node, 3)
                                      : jsgraph()->UndefinedConstant();
            Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Insert a construct stub frame into the chain of frame states. This will
            // reconstruct the proper frame when deoptimizing within the constructor.
            frame_state = CreateArtificialFrameState(
                node, frame_state, arity, BailoutId::ConstructStubInvoke(),
                FrameStateType::kConstructStub, shared, context);

            // This continuation just returns the newly created JSTypedArray. We
            // pass the_hole as the receiver, just like the builtin construct stub
            // does in this case.
            Node* const parameters[] = { jsgraph()->TheHoleConstant() };
            int const num_parameters = static_cast<int>(arraysize(parameters));
            frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared, Builtins::kGenericConstructorLazyDeoptContinuation,
                target, context, parameters, num_parameters, frame_state,
                ContinuationFrameStateMode::LAZY);

            Node* result = graph()->NewNode(javascript()->CreateTypedArray(), target, new_target,
                arg1, arg2, arg3, context, frame_state, effect, control);
            return Replace(result);
        }

        // ES #sec-get-%typedarray%.prototype-@@tostringtag
        Reduction JSCallReducer::ReduceTypedArrayPrototypeToStringTag(Node* node)
        {
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            NodeVector values(graph()->zone());
            NodeVector effects(graph()->zone());
            NodeVector controls(graph()->zone());

            Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
            control = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

            values.push_back(jsgraph()->UndefinedConstant());
            effects.push_back(effect);
            controls.push_back(graph()->NewNode(common()->IfTrue(), control));

            control = graph()->NewNode(common()->IfFalse(), control);
            Node* receiver_map = effect = graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
                receiver, effect, control);
            Node* receiver_bit_field2 = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map,
                effect, control);
            Node* receiver_elements_kind = graph()->NewNode(
                simplified()->NumberShiftRightLogical(),
                graph()->NewNode(simplified()->NumberBitwiseAnd(), receiver_bit_field2,
                    jsgraph()->Constant(Map::ElementsKindBits::kMask)),
                jsgraph()->Constant(Map::ElementsKindBits::kShift));

            // Offset the elements kind by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
            // so that the branch cascade below is turned into a simple table
            // switch by the ControlFlowOptimizer later.
            receiver_elements_kind = graph()->NewNode(
                simplified()->NumberSubtract(), receiver_elements_kind,
                jsgraph()->Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));

#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype)                                          \
    do {                                                                                   \
        Node* check = graph()->NewNode(                                                    \
            simplified()->NumberEqual(), receiver_elements_kind,                           \
            jsgraph()->Constant(TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); \
        control = graph()->NewNode(common()->Branch(), check, control);                    \
        values.push_back(jsgraph()->HeapConstant(                                          \
            factory()->InternalizeUtf8String(#Type "Array")));                             \
        effects.push_back(effect);                                                         \
        controls.push_back(graph()->NewNode(common()->IfTrue(), control));                 \
        control = graph()->NewNode(common()->IfFalse(), control);                          \
    } while (false);
            TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE

            values.push_back(jsgraph()->UndefinedConstant());
            effects.push_back(effect);
            controls.push_back(control);

            int const count = static_cast<int>(controls.size());
            control = graph()->NewNode(common()->Merge(count), count, &controls.front());
            effects.push_back(control);
            effect = graph()->NewNode(common()->EffectPhi(count), count + 1, &effects.front());
            values.push_back(control);
            Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
                count + 1, &values.front());
            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES #sec-number.isfinite
        Reduction JSCallReducer::ReduceNumberIsFinite(Node* node)
        {
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            Node* input = NodeProperties::GetValueInput(node, 2);
            Node* value = graph()->NewNode(simplified()->ObjectIsFiniteNumber(), input);
            ReplaceWithValue(node, value);
            return Replace(value);
        }

        // ES #sec-number.isfinite
        Reduction JSCallReducer::ReduceNumberIsInteger(Node* node)
        {
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            Node* input = NodeProperties::GetValueInput(node, 2);
            Node* value = graph()->NewNode(simplified()->ObjectIsInteger(), input);
            ReplaceWithValue(node, value);
            return Replace(value);
        }

        // ES #sec-number.issafeinteger
        Reduction JSCallReducer::ReduceNumberIsSafeInteger(Node* node)
        {
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            Node* input = NodeProperties::GetValueInput(node, 2);
            Node* value = graph()->NewNode(simplified()->ObjectIsSafeInteger(), input);
            ReplaceWithValue(node, value);
            return Replace(value);
        }

        // ES #sec-number.isnan
        Reduction JSCallReducer::ReduceNumberIsNaN(Node* node)
        {
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            Node* input = NodeProperties::GetValueInput(node, 2);
            Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input);
            ReplaceWithValue(node, value);
            return Replace(value);
        }

        Reduction JSCallReducer::ReduceMapPrototypeGet(Node* node)
        {
            // We only optimize if we have target, receiver and key parameters.
            if (node->op()->ValueInputCount() != 3)
                return NoChange();
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* key = NodeProperties::GetValueInput(node, 2);

            if (!NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect,
                    JS_MAP_TYPE))
                return NoChange();

            Node* table = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver,
                effect, control);

            Node* entry = effect = graph()->NewNode(
                simplified()->FindOrderedHashMapEntry(), table, key, effect, control);

            Node* check = graph()->NewNode(simplified()->NumberEqual(), entry,
                jsgraph()->MinusOneConstant());

            Node* branch = graph()->NewNode(common()->Branch(), check, control);

            // Key not found.
            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            Node* etrue = effect;
            Node* vtrue = jsgraph()->UndefinedConstant();

            // Key found.
            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            Node* efalse = effect;
            Node* vfalse = efalse = graph()->NewNode(
                simplified()->LoadElement(AccessBuilder::ForOrderedHashMapEntryValue()),
                table, entry, efalse, if_false);

            control = graph()->NewNode(common()->Merge(2), if_true, if_false);
            Node* value = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
            effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        Reduction JSCallReducer::ReduceMapPrototypeHas(Node* node)
        {
            // We only optimize if we have target, receiver and key parameters.
            if (node->op()->ValueInputCount() != 3)
                return NoChange();
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* key = NodeProperties::GetValueInput(node, 2);

            if (!NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect,
                    JS_MAP_TYPE))
                return NoChange();

            Node* table = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver,
                effect, control);

            Node* index = effect = graph()->NewNode(
                simplified()->FindOrderedHashMapEntry(), table, key, effect, control);

            Node* value = graph()->NewNode(simplified()->NumberEqual(), index,
                jsgraph()->MinusOneConstant());
            value = graph()->NewNode(simplified()->BooleanNot(), value);

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        namespace {

            InstanceType InstanceTypeForCollectionKind(CollectionKind kind)
            {
                switch (kind) {
                case CollectionKind::kMap:
                    return JS_MAP_TYPE;
                case CollectionKind::kSet:
                    return JS_SET_TYPE;
                }
                UNREACHABLE();
            }

        } // namespace

        Reduction JSCallReducer::ReduceCollectionIteration(
            Node* node, CollectionKind collection_kind, IterationKind iteration_kind)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            if (NodeProperties::HasInstanceTypeWitness(
                    broker(), receiver, effect,
                    InstanceTypeForCollectionKind(collection_kind))) {
                Node* js_create_iterator = effect = graph()->NewNode(
                    javascript()->CreateCollectionIterator(collection_kind, iteration_kind),
                    receiver, context, effect, control);
                ReplaceWithValue(node, js_create_iterator, effect);
                return Replace(js_create_iterator);
            }
            return NoChange();
        }

        Reduction JSCallReducer::ReduceCollectionPrototypeSize(
            Node* node, CollectionKind collection_kind)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            if (NodeProperties::HasInstanceTypeWitness(
                    broker(), receiver, effect,
                    InstanceTypeForCollectionKind(collection_kind))) {
                Node* table = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
                    receiver, effect, control);
                Node* value = effect = graph()->NewNode(
                    simplified()->LoadField(
                        AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()),
                    table, effect, control);
                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            }
            return NoChange();
        }

        Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext(
            Node* node, int entry_size, Handle<HeapObject> empty_collection,
            InstanceType collection_iterator_instance_type_first,
            InstanceType collection_iterator_instance_type_last)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // A word of warning to begin with: This whole method might look a bit
            // strange at times, but that's mostly because it was carefully handcrafted
            // to allow for full escape analysis and scalar replacement of both the
            // collection iterator object and the iterator results, including the
            // key-value arrays in case of Set/Map entry iteration.
            //
            // TODO(turbofan): Currently the escape analysis (and the store-load
            // forwarding) is unable to eliminate the allocations for the key-value
            // arrays in case of Set/Map entry iteration, and we should investigate
            // how to update the escape analysis / arrange the graph in a way that
            // this becomes possible.

            // Infer the {receiver} instance type.
            InstanceType receiver_instance_type;
            ZoneHandleSet<Map> receiver_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), receiver, effect,
                &receiver_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();
            DCHECK_NE(0, receiver_maps.size());
            receiver_instance_type = receiver_maps[0]->instance_type();
            for (size_t i = 1; i < receiver_maps.size(); ++i) {
                if (receiver_maps[i]->instance_type() != receiver_instance_type) {
                    return NoChange();
                }
            }
            if (receiver_instance_type < collection_iterator_instance_type_first || receiver_instance_type > collection_iterator_instance_type_last) {
                return NoChange();
            }

            // Transition the JSCollectionIterator {receiver} if necessary
            // (i.e. there were certain mutations while we're iterating).
            {
                Node* done_loop;
                Node* done_eloop;
                Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
                Node* eloop = effect = graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
                Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
                NodeProperties::MergeControlToEnd(graph(), common(), terminate);

                // Check if reached the final table of the {receiver}.
                Node* table = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
                    receiver, effect, control);
                Node* next_table = effect = graph()->NewNode(simplified()->LoadField(
                                                                 AccessBuilder::ForOrderedHashMapOrSetNextTable()),
                    table, effect, control);
                Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table);
                control = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

                // Abort the {loop} when we reach the final table.
                done_loop = graph()->NewNode(common()->IfTrue(), control);
                done_eloop = effect;

                // Migrate to the {next_table} otherwise.
                control = graph()->NewNode(common()->IfFalse(), control);

                // Self-heal the {receiver}s index.
                Node* index = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
                    receiver, effect, control);
                Callable const callable = Builtins::CallableFor(isolate(), Builtins::kOrderedHashTableHealIndex);
                auto call_descriptor = Linkage::GetStubCallDescriptor(
                    graph()->zone(), callable.descriptor(),
                    callable.descriptor().GetStackParameterCount(),
                    CallDescriptor::kNoFlags, Operator::kEliminatable);
                index = effect = graph()->NewNode(common()->Call(call_descriptor),
                    jsgraph()->HeapConstant(callable.code()), table, index,
                    jsgraph()->NoContextConstant(), effect);

                index = effect = graph()->NewNode(
                    common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), index,
                    effect, control);

                // Update the {index} and {table} on the {receiver}.
                effect = graph()->NewNode(
                    simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()),
                    receiver, index, effect, control);
                effect = graph()->NewNode(
                    simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()),
                    receiver, next_table, effect, control);

                // Tie the knot.
                loop->ReplaceInput(1, control);
                eloop->ReplaceInput(1, effect);

                control = done_loop;
                effect = done_eloop;
            }

            // Get current index and table from the JSCollectionIterator {receiver}.
            Node* index = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
                receiver, effect, control);
            Node* table = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
                receiver, effect, control);

            // Create the {JSIteratorResult} first to ensure that we always have
            // a dominating Allocate node for the allocation folding phase.
            Node* iterator_result = effect = graph()->NewNode(
                javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(),
                jsgraph()->TrueConstant(), context, effect);

            // Look for the next non-holey key, starting from {index} in the {table}.
            Node* controls[2];
            Node* effects[3];
            {
                // Compute the currently used capacity.
                Node* number_of_buckets = effect = graph()->NewNode(
                    simplified()->LoadField(
                        AccessBuilder::ForOrderedHashMapOrSetNumberOfBuckets()),
                    table, effect, control);
                Node* number_of_elements = effect = graph()->NewNode(
                    simplified()->LoadField(
                        AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()),
                    table, effect, control);
                Node* number_of_deleted_elements = effect = graph()->NewNode(
                    simplified()->LoadField(
                        AccessBuilder::ForOrderedHashMapOrSetNumberOfDeletedElements()),
                    table, effect, control);
                Node* used_capacity = graph()->NewNode(simplified()->NumberAdd(), number_of_elements,
                    number_of_deleted_elements);

                // Skip holes and update the {index}.
                Node* loop = graph()->NewNode(common()->Loop(2), control, control);
                Node* eloop = graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
                Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
                NodeProperties::MergeControlToEnd(graph(), common(), terminate);
                Node* iloop = graph()->NewNode(
                    common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop);

                Node* index = effect = graph()->NewNode(
                    common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), iloop,
                    eloop, control);
                {
                    Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index,
                        used_capacity);
                    Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop);

                    Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
                    Node* efalse0 = effect;
                    {
                        // Mark the {receiver} as exhausted.
                        efalse0 = graph()->NewNode(
                            simplified()->StoreField(
                                AccessBuilder::ForJSCollectionIteratorTable()),
                            receiver, jsgraph()->HeapConstant(empty_collection), efalse0,
                            if_false0);

                        controls[0] = if_false0;
                        effects[0] = efalse0;
                    }

                    Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
                    Node* etrue0 = effect;
                    {
                        // Load the key of the entry.
                        STATIC_ASSERT(OrderedHashMap::HashTableStartIndex() == OrderedHashSet::HashTableStartIndex());
                        Node* entry_start_position = graph()->NewNode(
                            simplified()->NumberAdd(),
                            graph()->NewNode(
                                simplified()->NumberAdd(),
                                graph()->NewNode(simplified()->NumberMultiply(), index,
                                    jsgraph()->Constant(entry_size)),
                                number_of_buckets),
                            jsgraph()->Constant(OrderedHashMap::HashTableStartIndex()));
                        Node* entry_key = etrue0 = graph()->NewNode(
                            simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
                            table, entry_start_position, etrue0, if_true0);

                        // Advance the index.
                        index = graph()->NewNode(simplified()->NumberAdd(), index,
                            jsgraph()->OneConstant());

                        Node* check1 = graph()->NewNode(simplified()->ReferenceEqual(), entry_key,
                            jsgraph()->TheHoleConstant());
                        Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
                            check1, if_true0);

                        {
                            // Abort loop with resulting value.
                            Node* control = graph()->NewNode(common()->IfFalse(), branch1);
                            Node* effect = etrue0;
                            Node* value = effect = graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
                                entry_key, effect, control);
                            Node* done = jsgraph()->FalseConstant();

                            // Advance the index on the {receiver}.
                            effect = graph()->NewNode(
                                simplified()->StoreField(
                                    AccessBuilder::ForJSCollectionIteratorIndex()),
                                receiver, index, effect, control);

                            // The actual {value} depends on the {receiver} iteration type.
                            switch (receiver_instance_type) {
                            case JS_MAP_KEY_ITERATOR_TYPE:
                            case JS_SET_VALUE_ITERATOR_TYPE:
                                break;

                            case JS_SET_KEY_VALUE_ITERATOR_TYPE:
                                value = effect = graph()->NewNode(javascript()->CreateKeyValueArray(), value,
                                    value, context, effect);
                                break;

                            case JS_MAP_VALUE_ITERATOR_TYPE:
                                value = effect = graph()->NewNode(
                                    simplified()->LoadElement(
                                        AccessBuilder::ForFixedArrayElement()),
                                    table,
                                    graph()->NewNode(
                                        simplified()->NumberAdd(), entry_start_position,
                                        jsgraph()->Constant(OrderedHashMap::kValueOffset)),
                                    effect, control);
                                break;

                            case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
                                value = effect = graph()->NewNode(
                                    simplified()->LoadElement(
                                        AccessBuilder::ForFixedArrayElement()),
                                    table,
                                    graph()->NewNode(
                                        simplified()->NumberAdd(), entry_start_position,
                                        jsgraph()->Constant(OrderedHashMap::kValueOffset)),
                                    effect, control);
                                value = effect = graph()->NewNode(javascript()->CreateKeyValueArray(),
                                    entry_key, value, context, effect);
                                break;

                            default:
                                UNREACHABLE();
                                break;
                            }

                            // Store final {value} and {done} into the {iterator_result}.
                            effect = graph()->NewNode(simplified()->StoreField(
                                                          AccessBuilder::ForJSIteratorResultValue()),
                                iterator_result, value, effect, control);
                            effect = graph()->NewNode(simplified()->StoreField(
                                                          AccessBuilder::ForJSIteratorResultDone()),
                                iterator_result, done, effect, control);

                            controls[1] = control;
                            effects[1] = effect;
                        }

                        // Continue with next loop index.
                        loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1));
                        eloop->ReplaceInput(1, etrue0);
                        iloop->ReplaceInput(1, index);
                    }
                }

                control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls);
                effect = graph()->NewNode(common()->EffectPhi(2), 3, effects);
            }

            // Yield the final {iterator_result}.
            ReplaceWithValue(node, iterator_result, effect, control);
            return Replace(iterator_result);
        }

        Reduction JSCallReducer::ReduceArrayBufferIsView(Node* node)
        {
            Node* value = node->op()->ValueInputCount() >= 3
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->UndefinedConstant();
            RelaxEffectsAndControls(node);
            node->ReplaceInput(0, value);
            node->TrimInputCount(1);
            NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView());
            return Changed(node);
        }

        Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
            Node* node, InstanceType instance_type, FieldAccess const& access)
        {
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect,
                    instance_type)) {
                // Load the {receiver}s field.
                Node* value = effect = graph()->NewNode(simplified()->LoadField(access),
                    receiver, effect, control);

                // See if we can skip the detaching check.
                if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
                    // Check whether {receiver}s JSArrayBuffer was detached.
                    Node* buffer = effect = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
                        receiver, effect, control);
                    Node* buffer_bit_field = effect = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()),
                        buffer, effect, control);
                    Node* check = graph()->NewNode(
                        simplified()->NumberEqual(),
                        graph()->NewNode(
                            simplified()->NumberBitwiseAnd(), buffer_bit_field,
                            jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)),
                        jsgraph()->ZeroConstant());

                    // TODO(turbofan): Ideally we would bail out here if the {receiver}s
                    // JSArrayBuffer was detached, but there's no way to guard against
                    // deoptimization loops right now, since the JSCall {node} is usually
                    // created from a LOAD_IC inlining, and so there's no CALL_IC slot
                    // from which we could use the speculation bit.
                    value = graph()->NewNode(
                        common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
                        check, value, jsgraph()->ZeroConstant());
                }

                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            }
            return NoChange();
        }

        namespace {
            uint32_t ExternalArrayElementSize(const ExternalArrayType element_type)
            {
                switch (element_type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
    case kExternal##Type##Array:                  \
        DCHECK_LE(sizeof(ctype), 8);              \
        return sizeof(ctype);
                    TYPED_ARRAYS(TYPED_ARRAY_CASE)
                default:
                    UNREACHABLE();
#undef TYPED_ARRAY_CASE
                }
            }
        } // namespace

        Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access,
            ExternalArrayType element_type)
        {
            size_t const element_size = ExternalArrayElementSize(element_type);
            CallParameters const& p = CallParametersOf(node->op());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* offset = node->op()->ValueInputCount() > 2
                ? NodeProperties::GetValueInput(node, 2)
                : jsgraph()->ZeroConstant();
            Node* value = (access == DataViewAccess::kGet)
                ? nullptr
                : (node->op()->ValueInputCount() > 3
                        ? NodeProperties::GetValueInput(node, 3)
                        : jsgraph()->ZeroConstant());
            Node* is_little_endian = (access == DataViewAccess::kGet)
                ? (node->op()->ValueInputCount() > 3
                        ? NodeProperties::GetValueInput(node, 3)
                        : jsgraph()->FalseConstant())
                : (node->op()->ValueInputCount() > 4
                        ? NodeProperties::GetValueInput(node, 4)
                        : jsgraph()->FalseConstant());

            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            // Only do stuff if the {receiver} is really a DataView.
            if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect,
                    JS_DATA_VIEW_TYPE)) {
                Node* byte_offset;

                // Check that the {offset} is within range for the {receiver}.
                HeapObjectMatcher m(receiver);
                if (m.HasValue()) {
                    // We only deal with DataViews here whose [[ByteLength]] is at least
                    // {element_size}, as for all other DataViews it'll be out-of-bounds.
                    JSDataViewRef dataview = m.Ref(broker()).AsJSDataView();
                    if (dataview.byte_length() < element_size)
                        return NoChange();

                    // Check that the {offset} is within range of the {byte_length}.
                    Node* byte_length = jsgraph()->Constant(dataview.byte_length() - (element_size - 1));
                    offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
                        byte_length, effect, control);

                    // Load the [[ByteOffset]] from the {dataview}.
                    byte_offset = jsgraph()->Constant(dataview.byte_offset());
                } else {
                    // We only deal with DataViews here that have Smi [[ByteLength]]s.
                    Node* byte_length = effect = graph()->NewNode(simplified()->LoadField(
                                                                      AccessBuilder::ForJSArrayBufferViewByteLength()),
                        receiver, effect, control);

                    if (element_size > 1) {
                        // For non-byte accesses we also need to check that the {offset}
                        // plus the {element_size}-1 fits within the given {byte_length}.
                        // So to keep this as a single check on the {offset}, we subtract
                        // the {element_size}-1 from the {byte_length} here (clamped to
                        // positive safe integer range), and perform a check against that
                        // with the {offset} below.
                        byte_length = graph()->NewNode(
                            simplified()->NumberMax(), jsgraph()->ZeroConstant(),
                            graph()->NewNode(simplified()->NumberSubtract(), byte_length,
                                jsgraph()->Constant(element_size - 1)));
                    }

                    // Check that the {offset} is within range of the {byte_length}.
                    offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
                        byte_length, effect, control);

                    // Also load the [[ByteOffset]] from the {receiver}.
                    byte_offset = effect = graph()->NewNode(simplified()->LoadField(
                                                                AccessBuilder::ForJSArrayBufferViewByteOffset()),
                        receiver, effect, control);
                }

                // Coerce {is_little_endian} to boolean.
                is_little_endian = graph()->NewNode(simplified()->ToBoolean(), is_little_endian);

                // Coerce {value} to Number.
                if (access == DataViewAccess::kSet) {
                    value = effect = graph()->NewNode(
                        simplified()->SpeculativeToNumber(
                            NumberOperationHint::kNumberOrOddball, p.feedback()),
                        value, effect, control);
                }

                // Get the underlying buffer and check that it has not been detached.
                Node* buffer = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
                    receiver, effect, control);

                if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
                    // Bail out if the {buffer} was detached.
                    Node* buffer_bit_field = effect = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()),
                        buffer, effect, control);
                    Node* check = graph()->NewNode(
                        simplified()->NumberEqual(),
                        graph()->NewNode(
                            simplified()->NumberBitwiseAnd(), buffer_bit_field,
                            jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)),
                        jsgraph()->ZeroConstant());
                    effect = graph()->NewNode(
                        simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached,
                            p.feedback()),
                        check, effect, control);
                }

                // Get the buffer's backing store.
                Node* backing_store = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()),
                    buffer, effect, control);

                switch (access) {
                case DataViewAccess::kGet:
                    // Perform the load.
                    value = effect = graph()->NewNode(simplified()->LoadDataViewElement(element_type),
                        buffer, backing_store, byte_offset, offset,
                        is_little_endian, effect, control);
                    break;
                case DataViewAccess::kSet:
                    // Perform the store.
                    effect = graph()->NewNode(simplified()->StoreDataViewElement(element_type),
                        buffer, backing_store, byte_offset, offset, value,
                        is_little_endian, effect, control);
                    value = jsgraph()->UndefinedConstant();
                    break;
                }

                // Continue on the regular path.
                ReplaceWithValue(node, value, effect, control);
                return Changed(value);
            }

            return NoChange();
        }

        // ES6 section 18.2.2 isFinite ( number )
        Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* input = NodeProperties::GetValueInput(node, 2);

            input = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                  NumberOperationHint::kNumberOrOddball, p.feedback()),
                input, effect, control);
            Node* value = graph()->NewNode(simplified()->NumberIsFinite(), input);
            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        // ES6 section 18.2.3 isNaN ( number )
        Reduction JSCallReducer::ReduceGlobalIsNaN(Node* node)
        {
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->TrueConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* input = NodeProperties::GetValueInput(node, 2);

            input = effect = graph()->NewNode(simplified()->SpeculativeToNumber(
                                                  NumberOperationHint::kNumberOrOddball, p.feedback()),
                input, effect, control);
            Node* value = graph()->NewNode(simplified()->NumberIsNaN(), input);
            ReplaceWithValue(node, value, effect);
            return Replace(value);
        }

        // ES6 section 20.3.4.10 Date.prototype.getTime ( )
        Reduction JSCallReducer::ReduceDatePrototypeGetTime(Node* node)
        {
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect,
                    JS_DATE_TYPE)) {
                Node* value = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSDateValue()), receiver,
                    effect, control);
                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            }
            return NoChange();
        }

        // ES6 section 20.3.3.1 Date.now ( )
        Reduction JSCallReducer::ReduceDateNow(Node* node)
        {
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* value = effect = graph()->NewNode(simplified()->DateNow(), effect, control);
            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // ES6 section 20.1.2.13 Number.parseInt ( string, radix )
        Reduction JSCallReducer::ReduceNumberParseInt(Node* node)
        {
            // We certainly know that undefined is not an array.
            if (node->op()->ValueInputCount() < 3) {
                Node* value = jsgraph()->NaNConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            int arg_count = node->op()->ValueInputCount();
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* object = NodeProperties::GetValueInput(node, 2);
            Node* radix = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3)
                                         : jsgraph()->UndefinedConstant();
            node->ReplaceInput(0, object);
            node->ReplaceInput(1, radix);
            node->ReplaceInput(2, context);
            node->ReplaceInput(3, frame_state);
            node->ReplaceInput(4, effect);
            node->ReplaceInput(5, control);
            node->TrimInputCount(6);
            NodeProperties::ChangeOp(node, javascript()->ParseInt());
            return Changed(node);
        }

        Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node)
        {
            if (FLAG_force_slow_path)
                return NoChange();
            if (node->op()->ValueInputCount() < 3)
                return NoChange();
            CallParameters const& p = CallParametersOf(node->op());
            if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
                return NoChange();
            }

            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* regexp = NodeProperties::GetValueInput(node, 1);

            // Check if we know something about the {regexp}.
            ZoneHandleSet<Map> regexp_maps;
            NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(broker(), regexp, effect, &regexp_maps);
            if (result == NodeProperties::kNoReceiverMaps)
                return NoChange();

            for (auto map : regexp_maps) {
                MapRef receiver_map(broker(), map);
                if (receiver_map.instance_type() != JS_REGEXP_TYPE)
                    return NoChange();
            }

            // Compute property access info for "exec" on {resolution}.
            AccessInfoFactory access_info_factory(broker(), dependencies(),
                graph()->zone());
            PropertyAccessInfo ai_exec = access_info_factory.ComputePropertyAccessInfo(
                MapHandles(regexp_maps.begin(), regexp_maps.end()),
                factory()->exec_string(), AccessMode::kLoad);
            if (ai_exec.IsInvalid())
                return NoChange();

            // If "exec" has been modified on {regexp}, we can't do anything.
            if (ai_exec.IsDataConstant()) {
                if (!ai_exec.constant().is_identical_to(
                        isolate()->regexp_exec_function())) {
                    return NoChange();
                }
            } else if (ai_exec.IsDataConstantField()) {
                Handle<JSObject> holder;
                // Do not reduce if the exec method is not on the prototype chain.
                if (!ai_exec.holder().ToHandle(&holder))
                    return NoChange();

                // Bail out if the exec method is not the original one.
                Handle<Object> constant = JSObject::FastPropertyAt(
                    holder, Representation::Tagged(), ai_exec.field_index());
                if (!constant.is_identical_to(isolate()->regexp_exec_function())) {
                    return NoChange();
                }

                // Protect the exec method change in the holder.
                Handle<Object> exec_on_proto;
                MapRef holder_map(broker(), handle(holder->map(), isolate()));
                Handle<DescriptorArray> descriptors(
                    holder_map.object()->instance_descriptors(), isolate());
                int descriptor_index = descriptors->Search(*(factory()->exec_string()), *holder_map.object());
                CHECK_NE(descriptor_index, DescriptorArray::kNotFound);
                holder_map.SerializeOwnDescriptors();
                dependencies()->DependOnFieldType(holder_map, descriptor_index);
            } else {
                return NoChange();
            }

            PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());

            // Add proper dependencies on the {regexp}s [[Prototype]]s.
            Handle<JSObject> holder;
            if (ai_exec.holder().ToHandle(&holder)) {
                dependencies()->DependOnStablePrototypeChains(
                    ai_exec.receiver_maps(), kStartAtPrototype,
                    JSObjectRef(broker(), holder));
            }

            effect = InsertMapChecksIfUnreliableReceiverMaps(
                result, regexp_maps, p.feedback(), regexp, effect, control);

            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* search = NodeProperties::GetValueInput(node, 2);
            Node* search_string = effect = graph()->NewNode(
                simplified()->CheckString(p.feedback()), search, effect, control);

            Node* lastIndex = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSRegExpLastIndex()), regexp,
                effect, control);

            Node* lastIndexSmi = effect = graph()->NewNode(
                simplified()->CheckSmi(p.feedback()), lastIndex, effect, control);

            Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(),
                jsgraph()->ZeroConstant(), lastIndexSmi);

            effect = graph()->NewNode(
                simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()),
                is_positive, effect, control);

            node->ReplaceInput(0, regexp);
            node->ReplaceInput(1, search_string);
            node->ReplaceInput(2, context);
            node->ReplaceInput(3, frame_state);
            node->ReplaceInput(4, effect);
            node->ReplaceInput(5, control);
            node->TrimInputCount(6);
            NodeProperties::ChangeOp(node, javascript()->RegExpTest());
            return Changed(node);
        }

        // ES section #sec-number-constructor
        Reduction JSCallReducer::ReduceNumberConstructor(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            Node* target = NodeProperties::GetValueInput(node, 0);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* value = p.arity() < 3 ? jsgraph()->ZeroConstant()
                                        : NodeProperties::GetValueInput(node, 2);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);

            // Create the artificial frame state in the middle of the Number constructor.
            SharedFunctionInfoRef shared_info = native_context().number_function().shared();
            Node* stack_parameters[] = { receiver };
            int stack_parameter_count = arraysize(stack_parameters);
            Node* continuation_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
                jsgraph(), shared_info,
                Builtins::kGenericConstructorLazyDeoptContinuation, target, context,
                stack_parameters, stack_parameter_count, frame_state,
                ContinuationFrameStateMode::LAZY);

            // Convert the {value} to a Number.
            NodeProperties::ReplaceValueInputs(node, value);
            NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt());
            NodeProperties::ReplaceFrameStateInput(node, continuation_frame_state);
            return Changed(node);
        }

        Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }

        Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }

        Factory* JSCallReducer::factory() const { return isolate()->factory(); }

        CommonOperatorBuilder* JSCallReducer::common() const
        {
            return jsgraph()->common();
        }

        JSOperatorBuilder* JSCallReducer::javascript() const
        {
            return jsgraph()->javascript();
        }

        SimplifiedOperatorBuilder* JSCallReducer::simplified() const
        {
            return jsgraph()->simplified();
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
